Dagger2 依赖的接力游戏(一): 什么是依赖和依赖注入?

Dagger2是用于软件解耦的依赖注入工具,由谷歌自dagger优化而来。在诸多的android框架中,属于相对复杂的一个,起码我自己在学习的过程当中,抓掉了不少的头发。虽然市面上有不少的介绍文章,浅显的、深入的都有,但是总感觉一次性讲齐全的还是比较少,需要东拼西凑看一大堆东西才能慢慢搭出一个框架来,还是非常痛苦的。所以在这里整理一下自己的理解,希望能够帮助到一些正在抓头的同学。当然要有最直观的理解,不管看了什么资料,还是要自己动手写点例子,按照自己的理解试试对错,然后再研究一下模板代码才行。

谁适合阅读本文

本文主要目的还是为Dagger2的入门同学提供一些帮助,将尝试从依赖关系传递的角度,从依赖的概念开始,解释到Dagger2的解决方案以及实现原理。如果你是dagger2的新手,那么阅读此文应该会有所收获,如果你已经对dagger2比较熟悉或者有深入的理解,也可以尝试着从本文的角度去理解dagger2的概念,看看是否有新的收获。最后欢迎大家讨论交流,如果文中有不足或者错误,希望大家能够予以指正。

写在前面

本文不讨论如何在android平台上应用dagger2,所有的示例都直接在GoogleSample的项目基础上直接书写,并且放到了github上面,代码分支与文章章节对应。本篇的代码放在项目的chapter1分支

Dagger2简介

dagger2是用于软件解耦的依赖注入工具,由谷歌自dagger优化而来。dagger的开发者就是大名鼎鼎的square公司,旗下还有retrofit,okhttp,greendao等等知名的开发框架。

dagger2使用注解和apt工具来生成注入依赖的模板代码,因此不会损耗运行时性能,适用于安卓平台。

dagger2使用生成模板代码的方式来注入依赖,使得依赖关系的错误,在编译阶段就能发现,并且模板代码相对简洁规范,可读性强,便于理解。

学习dagger2的关键还是要理解它产生的需求背景,它所要解决的问题,即通过依赖注入的方式,成为软件依赖关系的代理,从而实现软件架构的解耦。其相对复杂的注解功能以及生成的模板类,其实已经是解决依赖注入过程中存在的各种类型问题的一个最简洁方案了。接下来我们将从最简单的依赖问题,向复杂的依赖问题展开,梳理dagger2解决这些问题的方案以及实现原理,以及这个方案中依赖关系传递过程。

什么是依赖?

在OOP(面向对象编程)中,一个类需要另一个类的配合才能进行工作的情况,叫做依赖。依赖根据需求方和提供方的作用关系,又分为类依赖、成员依赖、方法依赖。

比如我们有一个汽车的类,它有名字和引擎。

public class Car {

    String mName;

    Engine mEngine;

    public String getName(){
        return mName;
    }

    Engine getEngine(){
        return mEngine;
    }

}

汽车有引擎,引擎有自己的气缸数目。

class Engine {

    public final int CYLINDER_FUEL_COST = 10;

    int mCylinderNumbers;

    public Engine(int cylinderNumbers){
       mCylinderNumbers = cylinderNumbers;
    }

    public int getCylinderNumbers(){
        return mCylinderNumbers;
    }

    public void run(Fuel fuel){
        fuel.burn(getCylinderNumbers() * CYLINDER_FUEL_COST);
    }
}

引擎的工作需要燃料

public class Fuel {

    int mEnergy;

    public void burn(int energy){
         mEnergy -= energy;
    }

}

我们还有一辆具体的汽车类型,蒙迪欧:

public class Mondeo extends Car {

    public Mondeo(){
        mName = "mondeo";
        mEngine = new Engine(4);
    }
}

在上述的一个简单的依赖模型中,Mondeo和Car存在继承关系,属于类依赖,类依赖属于直接依赖关系,会固化软件的依赖模型,这种依赖是在代码逻辑里写死的,没有修改代码以外的方法可以地改变这种依赖关系。如果依赖关系发生了变化,一定会引起类的变更,从而导致代码的改动。所以在OOP设计原则中,要严格控制继承关系,尽量使用组合的方式来复用逻辑。

Engine和Fuel属于方法依赖,方法依赖属于间接依赖,它所使用的对象在外部创建,调用方只关系对象的功能。因为创建逻辑在外部,因此对象可以被灵活地创建、初始化,应用多态等OOP方法,可以最大程度地避免业务逻辑的变更,导致软件系统的变更。

Car和Engine属于成员依赖,成员依赖是一个待定类型的依赖,如果一个类直接通过new的方式,初始化它的成员,比如我们的Mondeo,那么就会产生直接依赖,如果通过构造方法传递进来,那么就变成了方法依赖。

什么是依赖注入?

我们在Mondeo 类的内部直接创建了一个4缸引擎,这就产生了一个直接依赖,假设蒙迪欧有一天要升级,换成八缸的引擎,我们就不得不回来修改构造方法。如果我们优化一下:

public class Mondeo extends Car {

    public Mondeo(Engine engine){
        mName = "mondeo";
        mEngine = engine;
    }
}

public class Main {
    
    public static void main(String[] args){
        
        Engine engine = new Engine(4);
        
        Car car = new Mondeo(engine);
    }
}

这样引擎对象的创建过程就被放在了Car类的外部,我们对引擎的直接依赖,改为构造方法的间接依赖,这种将类的依赖需求,通过外部逻辑满足的方式,就叫做依赖注入。

(直接)依赖会产生什么问题,为什么需要依赖注入?

  • 假如被依赖的类发生修改,则所有的需求方也需要同步修改,并且这种修改还伴随着传递可能性,在复杂的软件当中,会导致牵一发而动全身的结果,使软件难以扩展、维护。

比如,假如我们的引擎有不同的品牌,引擎又有不同类型的气缸,气缸又有不同类型的轴承,假如这一个依赖关系链通过直接依赖的方式来实现,如果现在增加了一种轴承的类型,导致增加一系列的配件型号,那么我们需要修改依赖的每个环节的具体类型。而这种情况在真实的软件系统中会更加严重。

  • 假如一个类直接构造另一个类的对象,则这个类和另外一个类就形成了强制的绑定关系,无法独立地进行测试,降低代码的可测试性。

假如我们要统计汽车的油耗是否稳定,如果我们直接依赖了具体的引擎类型,那么如果出了问题,我们就无法得知是引擎的油耗问题,还是汽车的统计逻辑问题。如果我们采用注入依赖的方式,我们就可以通过一个Mock的引擎对象,替换真实的引擎对象,我们能够控制这个mock的对象,总是以稳定的油耗工作,从而剥离引擎问题对汽车油耗统计逻辑的影响。

小结

在本篇中,我们通过例子解释了几种依赖关系以及他们的依赖类型,解释了直接依赖会导致的问题,以及什么是依赖注入,为什么需要依赖注入。接下来我们会正式开始依赖关系的由简入繁,讲述这个过程中会出现的问题,对应的解决方案,以及原理。具体请看:

具体请看Dagger2 依赖的接力游戏(二)

参考文档

知乎: 神兵利器dagger2
github : Dagger2官方入口
Dagger 2 for Android Beginners
Dagger2入门解析

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