除了测试之外,为什么我们需要Dagger 2?

时间:2017-01-27 14:04:34

标签: java android dependency-injection dagger-2

此时,我对依赖注入(DI)的理解仅来自this article。我有兴趣尝试,但我只需澄清一些事情:

  1. 许多人将DI称为减少样板代码的工具。但根据该教程,Dagger 2的设置往往会为配置(模块和组件)创建更多类。我没有尝试过,但从它的外观来看,它并没有减少代码,它只是拆分它们,所以主类看起来更整洁。我错了吗?
  2. 尽管Dagger 2's claim DI不仅仅用于测试,但很多人都将其主要用于测试,包括Android's own guide。你有没有在生产就绪的应用程序中使用Dagger 2?这对你有用吗?
  3. 如果我对通过构造函数创建的传统依赖项非常熟悉,我是否还需要查看Dagger 2?我觉得这个库可能有能力改变我对RxJava的编码方式,我只是不确定它的好处和RxJava给我的一样多。
  4. 我知道,将Dagger与RxJava进行比较就像将苹果与橙子相比较。但从某种意义上说,它们都像Dagger一样是水果,而RxJava是可能使我的项目更大的第三方库。

2 个答案:

答案 0 :(得分:10)

你结合了两个单独的问题,应该分开评估:“为什么我们需要依赖注入?”和“为什么我们需要Dagger 2?”

依赖注入(控制反转)很有用,因为它允许组件的使用者提供组件的依赖关系。以日志格式化程序为例:如果没有依赖注入,您可能需要编写三个不同版本的类,这些版本会记录到stdout,远程服务器或文本文件。相比之下,如果你要写一个LogFormatter接受它写的Writer,那么你可以写一次并传入最合适的Writer,包括测试双(您制作的FakeWriter,或StringWriter,或模拟框架创建的mockWriter实例)进行测试。虽然它是为Guice而不是Dagger编写的,但我写了一篇separate SO answer来讨论依赖注入在生产使用和测试用例中的价值;你看到的大多数教程都会关注测试,假设“生产”和“测试”是你预先知道的两种情况,其他重用和再利用的机会将在以后出现。

一旦您接受了依赖注入为您提供的模块化,可重用性和可测试性优势,您可能会留下一个问题:如何管理这些极长的构造函数?毕竟,继续LogFormatter示例,如果不关心日志的去向,就无法编写应用程序。

MyApplication application = new MyApplication(
    new LoggingService(new LogFormatter(new StdOutWriter())),
    new DatabaseService(new MyApplicationDatabase(new File("my.db"))),
    ...);

这就是Dagger的亮点:它可以让您拥有依赖注入的所有好处,同时自动为您管理构造函数。这使得它可以封装创建对象的责任并使其更清晰,更安全,RxJava可以封装管理和传播异步事件的责任,使其更清洁,更安全。

重要的是要意识到Dagger 2的样板减少与手动依赖注入相比,而不是你要比较的原始构造函数调用。如果你坚持直接调用new,你可能会完全避免使用这个对象构建样板,但是你也会发现自己正在经历困难的杂技,试图将工作分解给多个开发人员或尝试测试或重用你编写的代码。集体的痛苦是为什么依赖注入如此受欢迎的概念,以及为什么像Spring,Guice和Dagger这样的图书馆越来越受欢迎。

我可以保证在几个特别大,众所周知且使用良好的生产Android应用程序中使用Dagger 2。 :)

答案 1 :(得分:1)

我只使用过Dagger 1,但这可能会以某种方式帮助你:

Dagger使用不同目标应用程序的模块构建(D)irected(a)循环(g)raph(也就是我的名字来源)。

因此,在一个应用程序项目中,我们能够构建四个不同的应用程序,与Jake Wharton的u2020应用程序非常相似。所有这些应用都有不同的目标。一个是内部测试,一个是测试团队(带截图功能),一个是内部发布,另一个是发布。

那么为什么我们需要四个不同的应用呢?答案是:所以我们不必在发布应用程序中使用调试或测试代码,我们可以将不属于的代码分开。

if(currentMode == Mode.DEBUG){
  Timber.i(...);
}

不再需要这样的东西了。

前面提到的屏幕截图功能不属于生产代码,所以它只在测试代码中。在构建应用程序时,框架使用了正确的模块并将其注入。

注意:我确信匕首还有其他用例。

编辑:这不会改变您以RxJava的方式编写代码的方式,但您必须在模块中大量使用DI。