使用Android依赖注入工具Dagger(一)

[翻译] 原文地址

在这个系列中,我将向你解释什么是依赖注入,为什么使用依赖注入以及在Android中如何使用依赖注入框架dagger, dagger是square出品的专门为Android优化设计的依赖注入框架。这篇文章是是我上一篇关于如何在Android项目中使用MVP模式的姊妹篇,读者中一定有人对在同一个项目中结合MVP模式和dagger感兴趣,我认为这两者可以完美的结合在一起。

开始的部分可能有些偏理论。知道什么是依赖注入以及为什么会有依赖注入很重要,不然的话很可能会觉得我们费那么大劲引入依赖注入很不值得。

什么是依赖

想要注入依赖,必须要先弄懂什么是依赖。简而言之,依赖就是两个模块之间的耦合,常见的情况是一个模块需要使用另外一个模块来完成某些任务。

为什么存在依赖是很危险的

模块的依赖是很危险的,因为一旦我们想要替换其中的一个模块,我们必须要更改和要替换的模块存在耦合的模块。尤其是当我们想创建一个可以测试的应用的时候,单元测试通常要求被测试的模块是和其他模块隔离开的。测试中通常是将非测试模块mock掉(相当于用假的实现相同接口的模块去替换)。看看下面的一段代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
public class Module1{
private Module2 module2;
public Module1(){
module2 = new Module2();
}
public void doSomething(){
...
module2.doSomethingElse();
...
}
}

我们如何去做到仅仅测试doSomething这个方法,而不用去关心doSomethingElse这个方法呢?如果测试失败了,那么又是哪个方法导致的呢?我们无法知道。如果doSomethingElse执行了保存到数据库相关的代码或者调用的其他的api,情况会变的更糟糕。

每一个new都是一个很难更改的依赖,我们应该尽可能的避免直接使用new。减少模块也不是一个避免过多依赖的好办法,别忘了单一职责原则(single responsibility principle)。

如何解决这个问题?依赖反转(dependency inversion)

既然不能使用new来直接创建依赖的对象,那就只好通过其他办法了。你猜是什么办法?yes,就是使用构造器来传入。这就是依赖反转的最基本的概念—不依赖具体的对象,依赖于抽象。
之前的那段代码可以改成这样:

1
2
3
4
5
6
7
8
9
10
11
12
13
public class Module1{
private Module2 module2;
public Module1(Module2 module2){
this.module2 = module2
}
public void doSomething(){
...
module2.doSomethingElse();
...
}
}

那么到底什么是依赖注入呢?

你猜对了,通过构造器传入依赖的对象就是依赖注入,这样就不需要在一个模块中去创建另外一个模块了。对象是在别的地方被创建然后通过构造器传入依赖它的对象中。

但是新的问题又出现了,如果我不能在一模块中创建另外一个模块,总要有一个地方来负责实例化这些模块吧。而且,如果我们创建了一个有很长构造器参数列表模块,代码将会变的令人恶心难读。这正是依赖注入器(independency injector)要帮助我们解决的。

什么是依赖注入器?

你可以把他想象成一个这样的模块,它负责构造所有其他模块的实例,并且传入这些模块的依赖。现在我们的应用程序中只有一个入口来负责创建所有模块的实例,而这个入口完全在我们的控制下。

什么是Dagger

Dagger是一个完全为低端设备设计的依赖注入器。绝大多数的依赖注入器都是基于反射来实现的。反射的确很酷,但是在低端设备上执行反射的效率太低。Dagger使用的是预编译技术来创建所有依赖的类。这样就避免了使用反射。Dagger虽然没有其他的依赖注入框架强大,但它是效率最高的。

Dagger仅仅是为了测试而使用么?

当然不是。Dagger将会让在其他app里面重用你的模块变的更加简单,甚至在同一个应用中修改摸个模块也会变的更加容易。比如一个应用,在开发版本中使用本地文件来获取数据,而在正式版本中,使用API 服务获取数据,使用依赖注入在不同的模式下注入不同的对象将会很完美的解决这个问题。

小结

我知道这篇文章很晦涩,但是解释清楚下面几篇将会使用的术语很重要。我们已经知道了什么是依赖,使用依赖反转的好处以及如何使用一个依赖注入框架去实现它。

接下来的几篇文章,让我们尽情的coding,以解手痒吧!