Dagger2——适用于Android和Java的快速依赖注入器
——简介,了解准备
一、依赖注入
在一个对象里面创建另一个对象的实例,这种行为是产生耦合的常见形式,对于一个大型项目来说过多的相互依赖会导致代码难以维护。
硬初始化(Hard init)是指在一个对象里面创建另一个对象的实例(new),与硬编码(Hard coding)一样都是糟糕代码滋生的方式,Hard init不仅增加了各个模块的耦合,还让单元测试变得更加困难。栗子如下。
这是业务核心咖啡机CoffeeMachine。
/**
*这是一个制作Coffee的例子
*CoffeeMaker是对制作Coffee过程的一个封装
*制作Coffee需要实现CoffeeMarker的makeCoffee方法
*/
public class CoffeeMachine {
private CoffeeMaker maker;
public CoffeeMachine(){
maker = new SimpleMaker(); //硬初始化(Hard init)
}
public String makeCoffee(){
return maker.makeCoffee();
}
}
CoffeeMaker
public interface CoffeeMaker {
String makeCoffee();
}
SimpleCoffeMaker
public class SimpleMaker implements CoffeeMaker {
@Override
public String makeCoffee() {
return "Coffee is made by SimperMarker";
}
}
当对该项目进行修改,使得咖啡机替换成咖啡师,则咖啡师有如下代码。
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的过程
}
}
SimpleMaker随之升级
public class SimpleMaker implements CoffeeMaker {
Cooker cooker; //现在需要咖啡师来制作咖啡了
public SimpleMaker(Cooker cooker){
this.cooker = cooker;
}
@Override
public String makeCoffee() {
return cooker.make();
}
}
从而导致了咖啡机也需要更改,即一个简单的业务升级可能导致大片业务代码变动,直接增加了测试的成本。
private CoffeeMaker maker;
//构造方法
public CoffeeMachine(Cooker cooker){
maker = new SimpleMaker(cooker);
}
类的初始化可以描述为new、get以及set。
-
new就是上面所描述的Hard init,容易增加各个模块之间的耦合。
-
get,可以看做工厂模式,工厂模式是new的一个升级版本,相对于Hard init来说,工厂模式把对象的创建都集中在工厂里,对于需要依赖的类来说,无需考虑对象的创建,只需要关注对象从工厂的获取。但是工厂模式并没有改变本质的依赖关系,对象的实现完全取决于工厂,导致原来的依赖由具体的对象变为相应的工厂,本质上还是有依赖关系。
-
set即为依赖注入,依赖注入的依赖是从外部传递过来的,而且在Java平台上很多时候都是通过反射或者动态编译来提供依赖注入,更加剥离了各个部分的耦合性。
依赖注入主要用三种途径:构造函数注入、Setter注入以及接口注入。
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就是一个实现依赖注入的工具。
二、反射简单介绍
反射是Java的特征之一,允许运行中的Java程序获取自身的信息,并且可以操作类或对象的内部属性。
通过反射,可以在运行时获取程序或者程序集中每一个类型的成员和成员信息。程序中一般的对象类型都是在编译期就确定下来,而Java反射机制可以动态地创建对象并调用其属性,这样的对象的类型在编译期是未知的。
反射的核心是JVM在运行时才动态加载类或调用方法/访问属性,不需要事先(写代码的时候或者编译器)知道运行对象是哪个。
Java反射主要提供以下功能:
- 在运行时判断任意一个对象所属的类;
- 在运行时判断任意一个类的对象;
- 在运行时判断任意一个类所具有的成员变量和方法(通过反射甚至可以调用private方法);
- 在运行时调用任意一个对象的方法;
反射的用途:反射在Java开发中应用十分的广泛,例如IDEA中输入一个对象或类并想调用它的属性或方法时,一按点号,编译器就会自动列出它的属性或方法,这就是使用到了反射。
Ps:反射的具体使用与介绍见资料博客与关于反射的笔记
Dagger2使用可以参照:Dagger2使用的简单介绍
依赖注入资料参考博客:依赖注入神器:Dagger2详解系列
反射资料参考博客:深入解析Java反射(1)- 基础
细说反射,Java和Android开发者必须跨越的坎