Skip to content

Latest commit

 

History

History
397 lines (327 loc) · 10 KB

3.Dagger2入门demo扩展(三).md

File metadata and controls

397 lines (327 loc) · 10 KB

Dagger2入门demo扩展(三)

上一篇文章中讲了一个入门的例子,感觉虽然不懂内部怎么实现的,好像大体知道要怎么去用了,理解了每部分都是干什么的,既然都讲了例子了,那就继续用 这个例子讲下如果被依赖的类的构造函数带有参数,我们该怎么去处理?

现在大夏天的我们平时去吧台打咖啡都想来点清凉的,透心凉,心飞扬,那怎么办?这时候我们是不是该提供一个加冰块的功能啊?

咖啡小姐姐把咖啡豆进行研磨成粉,然后放到咖啡机中,用热水和加压器去从咖啡粉中流过,这样就出来一小杯原酿咖啡了...,然后可以加点牛奶啊,加点糖啊。

好了,既然要加冰块,那就加呗,但是冰块放到哪里? 咖啡机要有一个放冰块的空间吧?而且还不能让冰块放到这里那么热不融化了吗? 所以要有个小冰箱啊.

public interface Ice {
	void add();
}

public interface IceBox {
	void addIce();
}

public class NajiIce implements  Ice{
    public NajiIce() {

    }
    @Override
    public void add() {
        System.out.println("加冰了,心飞扬");
    }
}

public class HaierIceBox implements  IceBox {
    Ice ice;
    public IceBox(Ice ice) {
        this.ice = ice;
    }
	@Override
    public void addIce() {
        ice.add();
    }
}

设计完了,那按照我们平时的使用,改造一下CoffeeMaker类:

class CoffeeMaker {
    private final Heater heater;
    private final Pump pump;
    private final IceBox iceBox;
    private Ice ice;

    CoffeeMaker() {
        heater = new ElectricHeater();
        pump = new Thermosiphon();
        ice = new NajiIce()
        iceBox = new HaierIceBox(ice);
    }

    public void brew() {
        heater.on();
        pump.pump();
        System.out.println(" [_]P coffee! [_]P ");
        iceBox.addIce();
        heater.off();
    }
}

好了,这样就可以了,执行一下:

04-20 17:12:01.626 20240-20240/com.charon.stplayer I/System.out: ~ ~ ~ heating ~ ~ ~
    => => pumping => =>
     [_]P coffee! [_]P 
    加冰了,心飞扬

那通过dagger2怎么使用呢?我们还要按照上一篇文章的步骤来:

  • 首先来到CoffeeModule类中,让他把IceIceBox管理起来,一定要记得引入Ice,因为IceBox依赖了Ice,这是一个依赖链条,要把这个链条上的所有依赖,都管理起来。
import dagger.Module;
import dagger.Provides;

@Module
public class CoffeeModule {
    @Provides
    Heater provideHeater() {
        return new ElectricHeater();
    }
    @Provides
    Pump providePump() {
        return new Thermosiphon();
    }
    @Provides
    Ice provideIce() {
        return new NanjiIce();
    }
    @Provides
    IceBox provideIceBox(Ice ice) {
        return new HaierIceBox(ice);
    }
}
  • 修改CoffeeComponent类,增加IceIceBox对应的方法:
@Component(modules = CoffeeModule.class)
public interface CoffeeComponent {
    void inject(CoffeeMaker maker);
    
    Heater provideHeater();
    Pump providePump();
    Ice provideIce();
    IceBox provideIceBox(Ice ice);
}
  • 在目标类中增加注入
class CoffeeMaker {
    @Inject
    Heater heater;
    @Inject
    Pump pump;
    @Inject
    IceBox iceBox;

    CoffeeMaker() {
        CoffeeComponent component = DaggerCoffeeComponent.create();
        component.inject(this);
    }

    public void brew() {
        heater.on();
        pump.pump();
        System.out.println(" [_]P coffee! [_]P ");
        iceBox.addIce();
        heater.off();
    }
}

好了,完成了,运行下吧,卧草,崩了!

写的很明白了,就是Compnonet中声明的方法不能带参数。 好,我们修改一下把provideIceBox(Ice ice)ice参数去掉:

@Component(modules = CoffeeModule.class)
public interface CoffeeComponent {
    void inject(CoffeeMaker maker);

    Heater provideHeater();
    Pump providePump();
    Ice provideIce();
    IceBox provideIceBox();
}

好了,再运行一下:

04-20 17:30:07.125 20916-20916/com.charon.stplayer I/System.out: ~ ~ ~ heating ~ ~ ~
    => => pumping => =>
     [_]P coffee! [_]P 
    加冰了,心飞扬

完美

总结一下:
如果被依赖类的构造函数带有参数,要把这个参数的类型也管理起来,而且Component接口中的方法不能带参数。

那么问题又来了,前面我们讲的例子都是依赖类只有一个构造函数,直接反射创建就好了,假如我们的依赖类有多个构造函数呢?该如何去选择根据不同的构造函数 生成对应的对象呢?

假设我们的咖啡机里又加新功能了,只加冰虽然凉了,但是口感不够丝滑,想再加点牛奶,享受丝滑般的感受。 好了新建一个牛奶类:

public class Milk {
    String type;
    public Milk() {
        type = "";
    }

    public Milk(String shuiguo) {
        type = shuiguo;
    }

    public void addMilk() {
        System.out.println("添加:" + type + "牛奶");
    }
}

接下来要用到一个比较重要的东西就是限定符,用来区分哪个构造函数new出来的对象:

@Qualifier//限定符
@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface Type {
    String value() default "";//默认值为""
}

这里插入一句,上面我们是使用@Qualifier自定义了一个限定符功能,其实这种返回值相同的情况是比较常见的,Dagger2中已经为我们提供了类似的自定义限定符@Named,我们可以直接使用@Named, 这里看一下它的实现:

@Qualifier
@Documented
@Retention(RUNTIME)
public @interface Named {

    /** The name. */
    String value() default "";
}

看到了吗? 和我们上面定义的Type注解一模一样。

接下来就是修改Moudle类,并且用上面我们创建的限定符@Type来区分不同的构造函数new出来的对象:

@Module
public class CoffeeModule {
    @Provides
    Heater provideHeater() {
        return new ElectricHeater();
    }

    @Provides
    Pump providePump() {
        return new Thermosiphon();
    }

    @Provides
    Ice provideIce() {
        return new NanjiIce();
    }

    @Provides
    IceBox provideIceBox(Ice ice) {
        return new HaierIceBox(ice);
    }

    @Provides
    @Type("normal")
    Milk provideNormalMilk() {
        return new Milk();
    }

    @Provides
    @Type("shuiguo") 
    Milk provideShuiGuoMilk(String shuiguo) {
        return new Milk(shuiguo);
    }

    //    由于Milk构造函数里使用了String,所以这里要管理这个String(★否则报错)
    //    int等基本数据类型是不需要这样做的
    @Provides
    public String provideString() {
        return new String();
    }
}

接下来继续修改Component类:

@Component(modules = CoffeeModule.class)
public interface CoffeeComponent {
    void inject(CoffeeMaker maker);

    Heater provideHeater();
    Pump providePump();
    Ice provideIce();
    IceBox provideIceBox();

    // 添加新提价的方法,注意这里也要用@Type声明,因为是通过@Type的值来去找不同的构造函数创建对象的
    @Type("normal")
    Milk provideNormalMilk();
    @Type("shuiguo")
    Milk provideShuiGuoMilk();
    String provideString();
}

我们在CoffeeMaker里面修改对应的代码:

import javax.inject.Inject;

class CoffeeMaker {
    @Inject
    Heater heater;
    @Inject
    Pump pump;
    @Inject
    IceBox iceBox;
    @Inject
    @Type("shuiguo") // 使用@Type来制定对应的构造函数
    Milk shuiguoMilk;

    CoffeeMaker() {
        CoffeeComponent component = DaggerCoffeeComponent.create();
        component.inject(this);
    }

    public void brew() {
        heater.on();
        pump.pump();
        System.out.println(" [_]P coffee! [_]P ");
        iceBox.addIce();
        shuiguoMilk.addMilk();
        heater.off();
    }
}

执行结果:

04-20 19:07:53.823 23151-23151/? I/System.out: ~ ~ ~ heating ~ ~ ~
    => => pumping => =>
     [_]P coffee! [_]P 
    加冰了心飞扬
    添加:牛奶

虽然是可以了,但是问题又来了,我想在创建Milk(String shuiguo)时往里面传递参数,例如,我传递个caomei。这该如何操作呢? 其实执行过程是这样的,再去Module中执行构造函数时候Milk(String shuiguo),他会发现可以传一个参数,然后他就会在该Module中去找返回值是string的方法, 并将该值传递给构造函数的参数中。

现在,我们重新把上面的CoffeeModule修改一下:

@Module
public class CoffeeModule {
    @Provides
    Heater provideHeater() {
        return new ElectricHeater();
    }

    @Provides
    Pump providePump() {
        return new Thermosiphon();
    }

    @Provides
    Ice provideIce() {
        return new NanjiIce();
    }

    @Provides
    IceBox provideIceBox(Ice ice) {
        return new HaierIceBox(ice);
    }

    @Provides
    @Type("normal")
    Milk provideNormalMilk() {
        return new Milk();
    }

    @Provides
    @Type("shuiguo")
    Milk provideShuiGuoMilk(String shuiguo) {
        return new Milk(shuiguo);
    }

    //    由于Milk构造函数里使用了String,所以这里要管理这个String(★否则报错)
    //    int等基本数据类型是不需要这样做的
    @Provides
    public String provideString() {
        return "caomei"; // 前面我们写的是new String(),这里改成caomei
    }
}

好了,执行下:

04-20 19:28:31.944 23549-23549/? I/System.out: ~ ~ ~ heating ~ ~ ~
    => => pumping => =>
     [_]P coffee! [_]P 
    加冰了,心飞扬
    添加:caomei牛奶

看,参数加上去了。

下一篇:4.Dagger2单例(四)