前言
依赖注入在这里的含义就是把一个类当作参数传入到另一个类中。在这一节提到,如果是想要在一个类中使用另一个类的资源,那么最好就是使用依赖注入。
优先考虑依赖注入来引用资源
1. 引出
首先来看一个例子,比如拼写检查其需要依赖词典,所以有时候我们的写法就是将词典作为一个静态对象,然后在工具类中调用词典进行检查:
public class SpellChecker {
//词典
private static final Lexicon dictionary = ...;
private SpellChecker(){}
//检测拼写
public static boolean isValid(String word){}
//建议
public static List<String> suggestions(String typo){}
}
又或者是这样写(把这个类设置成单例):
public class SpellChecker {
//词典
private static final Lexicon dictionary = ...;
private SpellChecker(...){}
public static SpellChecker INSTANCE = new SpellChecker(...);
//检测拼写
public static boolean isValid(String word){}
//建议
public static List<String> suggestions(String typo){}
}
但是以上两种方法都不太理想,因为上面的方法都是假设只有一本词典可以用,实际上不同国家的语法通常是不一样的,所以建议的方法是可以设置 dictionary 不是 final 的,然后添加一个方法,这个方法可以修改词典,但是这样写起来会容易出错,可能你检测一个单词就要修改一次词典,如果是多线程情况下就更加麻烦了。所以静态工具类和 Singleton 类不适合于需要引用底层资源的类。
2. 解决
当然了,有问题就会有解决的方法,这里我们解决的方法就是不同的实例对应不同的词典,比如中文和英文,那么就有两个实例,然后词典通过构造器传入这个实例中去,这就是依赖注入的一种形式:词典是拼写器的一个依赖,在创建拼写检查器的时候就将词典注入其中
public class SpellChecker {
//词典
private final Lexicon dictionary;
public SpellChecker(Lexicon dictionary){
this.dictionary = dictionary;
}
//检测拼写
public static boolean isValid(String word){}
//建议
public static List<String> suggestions(String typo){}
}
这就是依赖注入模式,虽然这个拼写检查器的范例中只有一个资源,但是依赖注入适合于任何数量的资源以及任意的依赖形式。所以可以看到的是,我们如果想要完成词典检查器的完整功能,只需要使用一个 List 或者一个 Map 把词典存起来,然后进行调用即可。
3. 依赖注入
上面的例子中,依赖注入的对象资源具有不可变性,里面没有提供可以修改词典的方法。所以多个客户端可以共享依赖对象,依赖注入也同样适合用于构造器、静态工厂和构建器。
这种模式还可以用于工厂方法中,将资源工厂传给构造器,使用工厂来进行创建实例。虽然依赖注入极大地提高了灵活性和可测试性,但是它会导致大型项目非常凌乱,因为一个大型项目通常包含上千个依赖,所以这时候就需要用到依赖注入框架了,像是 Dagger、Guice 或者 Spring。
总之,当一个类需要使用另一个类的资源的时候,不要使用 Singleton 和静态工具类来实现依赖一个或者多个底层资源的类,且资源的行为会影响到类的行为,也不要在这个类中直接创建这些资源,而是应该通过构造器进行依赖注入。
如有错误,欢迎指出!!!!