Android中的依赖注入类型和选择

Android中的依赖注入类型和选择

优势:

  • Reusability of code
  • Ease of refactoring
  • Ease of testing

什么是依赖注入

举个不是依赖注入的例子

class Car {

    private Engine engine = new Engine();

    public void start() {
        engine.start();
    }
}


class MyApp {
    public static void main(String[] args) {
        Car car = new Car();
        car.start();
    }
}

在这个例子中,Car 类构造了它自己的Engine,这将会有这些问题:

  • Car和Engine 紧密耦合,一个Car的实例只能使用一种Engine,没有子类或者可替代的实现可以被简单的使用。如果Car构建了自己的Engine,当你要使用Gas或者Electric 类型的Engines的时候你将创建两种类型的Car,而不是重用相同的Car,
  • 会使测试更加的困难

什么样的代码看起来像是依赖注入呢?取代Car初始化的时候构建自己的Engine,以接收Engine对象作为一个参数的方式来代替。

class Car {

    private final Engine engine;

    public Car(Engine engine) {
        this.engine = engine;
    }

    public void start() {
        engine.start();
    }
}


class MyApp {
    public static void main(String[] args) {
        Engine engine = new Engine();
        Car car = new Car(engine);
        car.start();
    }
}

在main方法中 先创建Engine实例然后作为参数来构造Car实例。这种基于依赖注入方法的好处是:

  • Car类可以重复利用,你可以传递不同实现的Engine给Car,比如说你可以定义一个你想让Car使用的Engine子类ElectricEngine,如果你使用依赖注入,你所需要做的仅仅是将上述代码中的Engine实例更新为ElectricEngine实例,Car的代码不需要做任何改动仍然可以运作。
  • Car类更容易进行测试

Android中两种主要的依赖注入方式:

  • 构造器注入。这是上面code所描述的方式,你通过一个类的构造器给它传递依赖

  • 字段注入(或者Setter注入)。一些Android Framework 类比如说Activity类和Fragment类已经被系统实例化了,所以构造器注入是不可行的,通过字段注入,依赖项的实例化可以在这些类被创建后,代码如下。

class Car {

    private Engine engine;

    public void setEngine(Engine engine) {
        this.engine = engine;
    }

    public void start() {
        engine.start();
    }
}

class MyApp {
    public static void main(String[] args) {
        Car car = new Car();
        car.setEngine(new Engine());
        car.start();
    }
}

自动依赖注入

在上面的示例中,你自己创建,提供和管理了不同类的依赖关系,而不需要依赖库。这称之为手动依赖注入,

Car的例子中只有一个依赖项,但是更多的依赖项和类用手动依赖注入将会变得冗长乏味。手动依赖注入也显示出了几个问题:

  • 对于大型App,获取所有依赖项并正确连接它们可能需要大量的样板代码。 在多层体系结构中,为了为顶层创建对象,您必须提供其下层的所有依赖关系。 举一个具体的例子,要制造一辆真正的汽车,您可能需要引擎,变速箱,底盘和其他零件。 发动机又需要气缸和火花塞。
  • 当您无法在传递依赖项之前构造依赖项时(例如,在使用延迟初始化或将对象作用域确定为应用程序流时),您需要编写并维护一个自定义容器(或依赖关系图),以管理您的生命周期 内存中的依赖项。

有一些库通过自动化创建和提供依赖项的过程来解决这个问题。它们可分为两类:

  • 基于反射的解决方案,在运行时连接依赖项。
  • 静态解决方案,在编译时生成代码连接依赖项。

Dagger 是一个流行的依赖注入库,可用于Java,Kotlin,Android,它由谷歌维护。Dagger通过创建和管理依赖关系图,让你在App中使用依赖注入提供了便利。它提供完全静态和编译时依赖关系,解决了基于反射解决方案(例如Guice)的许多开发和性能问题。

依赖项注入的替代方法

依赖项注入的替代方法是使用服务定位器。 服务定位器设计模式还改善了类与具体依赖关系的解耦。 您创建一个称为服务定位器的类,该类创建并存储依赖项,然后根据需要提供这些依赖项。

class ServiceLocator {

    private static ServiceLocator instance = null;

    private ServiceLocator() {}

    public static ServiceLocator getInstance() {
        if (instance == null) {
            synchronized(ServiceLocator.class) {
                instance = new ServiceLocator();
            }
        }
        return instance;
    }

    public Engine getEngine() {
        return new Engine();
    }
}

class Car {

    private Engine engine = ServiceLocator.getInstance().getEngine();

    public void start() {
        engine.start();
    }
}

class MyApp {
    public static void main(String[] args) {
        Car car = new Car();
        car.start();
    }
}

服务定位器模式与依赖项注入的不同之处在于元素的使用方式。 使用服务定位器模式,类可以控制并要求注入对象; 通过依赖注入,该应用程序可以控制并主动注入所需的对象。

与依赖注入对比:

  • 服务定位器所需的依赖项集合使代码更难测试,因为所有测试都必须与相同的全局服务定位器进行交互。
  • 依赖项是在类实现中编码的,而不是在API表面中。因此,很难从外部了解一个类需要什么。因此,对Car或服务定位器中可用的依赖项的更改可能导致引用失败,从而导致运行时或测试失败。
  • 如果您希望将范围扩大到整个应用程序的生存期以外的任何地方,那么管理对象的生存期就比较困难。

为您的应用选择正确的技术

如上所述,这里有集中不同的技术去管理你的应用的依赖:

  • 手动依赖注入 仅适用于相对小的app,因为它的扩展性差,当项目变得庞大,传递对象会要求很多模板代码。
  • 服务定位器 从相对较少的样板代码开始,但是扩展性也很差。此外,测试变得更加困难,因为它们依赖于单例对象。
  • Dagger 为扩展而创建。它非常适合构建复杂的应用程序
项目大小
使用的工具 手动注入 服务定位器 Dagger Dagger Dagger

如果你的小型app 似乎有可能增长,当没有太多代码需要修改时。你应该趁早考虑迁移到Dagger。

总结

依赖注入可以给你的app提供以下优势:

  • 类的可重用性和依赖解耦:换出依赖项的实现会更容易。 由于控制反转,因此代码重用得到了改善,并且类不再控制其依赖关系的创建方式,而是可以与任何配置一起使用。

  • 易于重构:依赖关系成为API表面的可验证部分,因此可以在对象创建时或在编译时对其进行检查,而不必将其隐藏为实现细节。

  • 易于测试:一个类部管理他自己的依赖,因此当你测试它的时候,你可以传给它不同实现的实现去测试你不同的的用例

为了更好的立即依赖注入的好处,你应该在你的代码中手动尝试一下。

资料来源

https://developer.android.google.cn/training/dependency-injection?hl=zh_cn

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,245评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,749评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,960评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,575评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,668评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,670评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,664评论 3 415
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,422评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,864评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,178评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,340评论 1 344
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,015评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,646评论 3 323
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,265评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,494评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,261评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,206评论 2 352

推荐阅读更多精彩内容

  • 一、首先你要知道什么是依赖? 想要理解Dagger2,首先你要理解一个概念,就是什么是依赖,懂的同学可以省过此段。...
    为梦想战斗阅读 440评论 0 0
  • 文接《Dagger2 依赖的接力游戏(一)》 本篇代码收录在项目的chapter2分支 接下来我们要讨论依赖关系及...
    散落_a0b3阅读 1,501评论 0 0
  • 学习资料来自 Angular.cn 与 Angular.io。 本章在线例子 依赖注入 (Dependency i...
    小镭Ra阅读 1,462评论 0 2
  • 什么是依赖注入呢? 依赖注入(DI)是一种非常流行的设计模式在许多的语言之中,比如说Java和C#,但是它似乎并没...
    木易林1阅读 1,427评论 0 0
  • 诺贝尔经济学奖得主戈里·贝尔曾说:“因政府介入了经济,才衍生了腐败,介入越多,贪污贿赂就越严重,哪个国家都如此。”...
    柳阿莹啊阅读 407评论 0 0