本文翻译自 Dagger 2 for Android Beginners — DI part I。
目录
这个故事是该系列的第二部分,适用于 Android 初学者的 Dagger 2。 如果您没有阅读上一篇文章,可以从这里开始。
系列目录
- 适用于 Android 初学者的 Dagger 2 - 简介
- 适用于 Android 初学者的 Dagger 2 - DI 第一部分(你在这儿)
- 适用于 Android 初学者的 Dagger 2 - DI 第二部分
- 适用于 Android 初学者的 Dagger 2 - Dagger 2 第一部分
- 适用于 Android 初学者的 Dagger 2 - Dagger 2 第二部分
- 适用于 Android 初学者的 Dagger 2 - Dagger 2 进阶第一部分
- 适用于 Android 初学者的 Dagger 2 - Dagger 2 进阶第二部分
Dagger 2 前情回顾..
在上一章节,我们讨论了依赖关系。我们了解了依赖性意味着什么,它的类型以及它们如何影响我们的软件开发和代码维护。
先决条件
我假设您熟悉编程中的依赖关系(上一篇),Java 编程语言,OOP 概念和 Android 开发。
注意:如果你对我讲故事的方式感到不满意 - 将 权力的游戏 作为概念,请在继续时随意更改类的名称。
什么是依赖注入?
以前我们理解 依赖 的含义及其对我们项目的影响。现在我们将理解 依赖注入 的含义。
在我们跳转到DI之前,我们需要弄清楚解决方案 - 避免进入依赖关系所构成的陷阱。 换句话说,硬依赖问题(不可重复使用和可测试的代码)就像 White Walkers,我们不应该加入他们的军队 - 相反,我们需要找到方法来打败他们。
不要加入他们的军队
解决硬性依赖问题(又名 white walkers)的策略
我们需要一个可靠的计划来解决或避免/破坏硬依赖问题。 该策略或计划称为依赖注入(DI)。 换句话说,要杀死 white walkers,你可以用火烧它们或使用龙玻璃。 类似地,为了避免硬依赖,您需要使用或遵循依赖注入技术。
DI 只是避免依赖性的技术之一。 还有很多其他方法。 对于 Android 开发,这种方法被认为不太容易,许多大型生产应用程序都使用这种策略。
依赖注入技术
那么,这种DI技术是什么?
依赖注入是一种技术,其中一个对象提供另一个对象的依赖关系。 依赖项是可以使用的对象(服务)。 注入是将依赖项传递给将使用它的依赖对象(客户端)。 该服务是客户端状态的一部分。 将服务传递给客户端,而不是允许客户端构建或查找服务,是模式的基本要求。(来源)
当我们将此与 GOT 进行比较时,Cersei 正准备迎接大战。 她没有烧掉她王国的所有现金,而是试图从布拉沃斯的 iron bank 获得贷款。 在这里,现金是依赖 - 所有房屋都需要(房屋取决于战争的现金)。 因此,外部对象(iron bank)将向依赖者(房屋)注入依赖(现金)。
Lannisters 向 iron bank 申请现金(依赖)
换句话说,依赖注入建立在控制反转的概念之上。 这说明一个类应该从外部获得它的依赖。 简单来说,没有类应该实例化另一个类,而是应该从配置类中获取实例。
例1
说的够多了...让我们深入研究一些代码吧。 现在我们将看到一个小例子,我们将看到两个场景。 首先,我们将创建几个具有硬依关系的类(不使用 DI)。 然后我们将遵循依赖注入并按计划将事物分离。
场景1:没有依赖注入
问题:私生子之战 - 斯塔克斯和博尔顿正在为控制北方的战争做准备。 我们需要做好准备并将它们带入战争中。
私生子之战
由于我们的示例将/可能涉及许多家族,让我们创建一个名为 House 的通用接口,并让所有其他家族实现它。 在我们的界面中我已经包含了几个方法 - prepareForWar( ) 和reportForWar( )(忽略幼稚的函数:-P)。
public interface House {
void prepareForWar();
void reportForWar();
}
接下来,我们需要为我们的两个家族创建类—— Starks 和 Boltons。让我们创建它们并实现接口 House。
public class Starks implements House {
@Override
public void prepareForWar() {
//do something
System.out.println(this.getClass().getSimpleName()+" prepared for war");
}
@Override
public void reportForWar() {
//do something
System.out.println(this.getClass().getSimpleName()+" reporting..");
}
}
public class Boltons implements House {
@Override
public void prepareForWar() {
//do something
System.out.println(this.getClass().getSimpleName()+" prepared for war");
}
@Override
public void reportForWar() {
//do something
System.out.println(this.getClass().getSimpleName()+" reporting..");
}
}
注意:this.getClass().getSimpleName()
只返回类名
接下来,我们需要把这两个家族带到战争中去。让我们创建 War 类,并要求他们做好准备按时报告。
public class War {
private Starks starks;
private Boltons boltons;
public War(){
starks = new Starks();
boltons = new Boltons();
starks.prepareForWar();
starks.reportForWar();
boltons.prepareForWar();
starks.reportForWar();
}
}
分析
War
让我们考虑 War 类的构造函数 - 它需要其他类(所有的家族),这里我们有2个类 Starks 和 Boltons。 这两个类准备好并到战场报到。
在设计良好的面向对象的应用程序中,每个对象只有很少的职责,并依赖其他对象来完成大部分工作。 War 类依赖于 Starks
和 Boltons
。 所以这些是 War 类的依赖。 没有这些,War 就不会发生。 在对象开始执行实际工作之前,必须设法满足其所有依赖项。 我们的 War 的依赖关系,通过在其构造函数中创建它们的新实例来满足。
虽然在构造函数上实例化依赖项对于小型应用程序来说效果很好,但随着应用程序的发展,它会带来许多缺点。 首先,它使 class 相当不灵活。 例如,如果这个 app 应该在多个平台上运行,我们可能想要将 Boltons 与另一个交换,但这不容易做到。 也许,我们也希望在多个对象之间共享相同的 Boltons,但除非修改了类,否则这也很难实现。
其次,不可能孤立地测试我们的类。 构建单个 War 将始终自动构造另外两个对象,这些对象最终将与原始对象一起进行测试。 如果其中一个依赖项依赖于昂贵的外部资源(如 Allies),或者它本身具有大量依赖项,那么这可能是一个严重的问题。
如前所述,硬依赖就像在没有适当武器的情况下与 White Walkers 作战。
总结
在这一站中,我们详细讨论了依赖和依赖注入-没有类应该实例化另一个类,而是应该从配置类中获取实例。
同样,依赖注入是一种解决或避免硬依赖问题的方法,通过从外部源-提供类所需的依赖关系,每个类都变得独立、易于测试和可读。
我们还看到了一个硬依赖问题的例子,创造了 私生子之战 的场景。
下一步是什么?
在下一站,我们将使用依赖注入将 War 类解耦 - 使用传统方法。