微信号:gh_a58345f58216

介绍:Android高手进阶之路,让我们共同学习,每天进步一点点.http://wuxiaolong.me/

详解Dagger2系列之开始篇:磨刀不误砍柴工

2016-07-14 14:03 ITKobe24

作者:ITKobe24

原文链接:https://dreamerhome.github.io/2016/07/07/dagger/

由于微信公众号不支持外链,要想了解更多,可以点击底部「阅读原文」跳转至原文。

序言

Dagger2是啥

Dagger2是啥,Google告诉我们:

Dagger is a fully static, compile-time dependency injection framework for both Java and Android. It is an adaptation of an earlier versioncreated by Square and now maintained by Google.
Dagger aims to address many of the development and performance issues that have plagued reflection-based solutions. More details can be found in this talk(slides) by +Gregory Kick.
那么长一段话,看的我是泪流满面:
Dagger是为Android和Java平台提供的一个完全静态的,在编译时进行依赖注入的框架,原来是由Square公司维护的然后现在把这堆东西扔给Google维护了。Dagger解决了基于反射带来的开发和性能上的问题(因为Dagger并没有用反射来做依赖注入)blabla。。。。说了那么多,其实就是告诉我们这家伙可以用来搞依赖注入哦
依赖注入,搞过Spring的人肯定都知道这是啥,SpringMVC里用到了大量依赖注入,鉴于gay们都是做Android,让我们共饮此杯,聊一聊依赖注入,熟悉依赖注入的同学戳这里
(下一篇:详解Dagger2系列之撸码篇:横看成岭侧成峰)。

依赖注入

我们在做项目时,经常需要在一个对象里去创建另一个对象的示例,这种行为是产生耦合的常见形式,对于一个大型项目来说,过多的相互依赖会导致代码难以维护,很容易就会碰到修改一个小需求需要大面积的修改各种代码,特别是代码原来不是自己维护的,撸着代码的你就开始问候别人家的亲友了。。。。

举个栗子
  我们现在有家Coffee Shop

这个是我们的业务核心咖啡机CoffeeMachine

 /**
 * 这是一个制作Coffee的例子
 * CoffeeMaker是对制作Coffee过程的一个封装
 * 制作Coffee需要实现CoffeeMarker的makeCoffee方法
 */
public class CoffeeMachine {
    private CoffeeMaker maker;
    public CoffeeMachine(){
        maker = new SimpleMaker();
    }
    public String makeCoffee(){
        return maker.makeCoffee();
    }
}

CoffeeMaker

public interface CoffeeMaker {
    String makeCoffee();
}

SimpleCoffeeMaker

public class SimpleMaker implements CoffeeMaker {
    @Override
    public String makeCoffee() {
        return  "Coffee is made by SimperMarker";
    }
}

小店新开张,我们的咖啡都是咖啡机做出来的,物美价廉啦。。。。

这家CoffeeShop很简单,在CoffeeMachine中可以看到,CoffeeMachine持有了一个CoffeeMaker接口,而具体制作Coffee的过程是由实现了CoffeeMaker的自动咖啡机SimpleMaker实现的,CoffeeMaker是在构造方法中new 出了一个实现CoffeeMaker接口的SimpleCoffee。当前的功能很简单,这么写看着也没什么问题。

随着业务的扩展,消费人群改变了,自动咖啡机也完全不能满足现有客户的需求,这个时候我们的CoffeeShop该进行业务升级咯 。

经过董事会决定,公司决定投入大笔资金,雇佣咖啡师来制作咖啡。
  我们的咖啡师:

public class Cooker {
    String name; //咖啡师名字
    String coffeeKind; //制作咖啡的类型

    public Cooker(String name,String coffeeKind){
        this.name = name;
        this.coffeeKind = coffeeKind;
    }
    public String make(){
        return name +" make " + coffeeKind; //咖啡师制作Coffee的过程
    }
}

这时候SimpleMarker升级了  :

public class SimpleMaker implements CoffeeMaker {
    Cooker cooker;  //现在需要咖啡师来制作咖啡了
    public SimpleMaker(Cooker cooker){
        this.cooker = cooker;
    }
    @Override
    public String makeCoffee() {
        return cooker.make();
    }
}

基于目前的情况,我们制作咖啡的流程发生了变化,原来的业务随着Cooker的加入发生了改变,但细心的小伙伴会发现目前还有一个地方受到了影响,那就是我们的CoffeeMachie,看这段代码:

private CoffeeMaker maker;
//构造方法  
public CoffeeMachine(Cooker cooker){
    maker = new SimpleMaker(cooker);
}

我们的SimpleMake升级了,业务波动影响到了我们的CoffeeMachine,这时候不得不对CoffeeMachine也进行修改:

public class CoffeeMachine {
    private CoffeeMaker maker;
    public CoffeeMachine(Cooker cooker){
        maker = new SimpleMaker(cooker);
    }
    public String makeCoffee(){
        return maker.makeCoffee();
    }
}

这时候我们的CoffeeMachine就懵逼了,你的业务升级就升级呗,为毛我制造CoffeeMachine的过程也要变动,非常的不愿意。。但迫于老板的压力。最后还是给整改了。很明显,这是一个不合适的流程,简单的一个业务的升级,还要找我们的机器制造厂来帮你修改,那如果业务非常复杂,引用了SimpleMaker的可不仅仅是CoffeeMachine一个,那是不是每个引用的地方都需要进行修改,业务庞大的情况下,这种修改就是致命的,不仅需要做大量没有意义的体力劳动来修改,还可能导致大片业务代码的变动直接增加测试的成本,其他接收这个需求的开发GG直接得跪键盘了,一个SimpleMakere的改动对CoffeeMachine产生了直接的影响,肯定有什么地方是不对的。原因就是CoffeeMachine里的CoffeeMaker是自己new出来的。这就是一个很不好的地方。
  这种糟糕的实例引用的方式我们称之为硬初始化(Hard init),和硬编码(Hard coding)一样,都是糟糕代码滋生的好方法,Hard init不仅增加了各个模块的耦合,还让单元测试变得更加困难了。
  就在这时,我们的依赖注入降临了。依赖注入主要有三种途径,eg

public class CoffeeMachinWithInjection implements InjectMaker{
    private CoffeeMaker maker;
    /*依赖注入的3种常见形式
     *No.1  构造函数注入
     */
    public CoffeeMachinWithInjection(CoffeeMaker maker){
        this.maker = maker;
    }
    //No.2  Setter注入
    public void setMaker(CoffeeMaker maker){
        this.maker = maker;
    }
    // //No.3 接口注入
    @Override
    public void injectMaker(CoffeeMaker maker) {
        this.maker = maker;
    }
    public String makeCoffee(){
        return maker.makeCoffee();
    }
}

其中,InjectMarker接口内容如下:

public interface InjectMaker {
    void injectMaker(CoffeeMaker maker);
}

是不是很简单,说白了就是不要在需要依赖的类中通过new来创建依赖而是通过方法提供的参数注入进来,这样我们的需要依赖的类和提供依赖的类的实现方法分隔开了,一切又变得如此美好咯。
不过这种手动提供依赖也是很繁杂的工作,充满的浓浓的重复体力劳动的气息,如何来尽量减少这些冗余代码的制作呢,答案就是Dagger!

第一部分到此结束。

下一篇:详解Dagger2系列之撸码篇:横看成岭侧成峰


欢迎长按上图 识别图中二维码或者微信扫一扫关注我的公众号。

 
AndroidProgrammer 更多文章 一个程序猿的幸福历程 Android开发之MVP模式(根据google的demo的修改版) Android Toast源码解析 Android完全退出和全局异常捕获 Android CustomView
猜您喜欢 发现意外之美 - SwiftyJSON 源码学习(2) | 咖啡时间 大话插件 - 类加载机制 关于 “混圈子” 和 “群交流” .NET 跨平台(CentOS )相关文档整理 设计师+产品经理的爱情!