优先考虑依赖注入来引入资源
有许多类会依赖一个或者多个底层的资源,例如:拼写检查器需要依赖词典。因此,像下面这种把类实现为静态工具类的做法很常见。
public class SpellChecker{
private static final Lexicon dictionary = ...;
private SpellChecker(){
}
public static boolean isValid(String word){
}
public static List<String> suggestions(String typo){
}
}
这样做的闭端非常之明显,所依赖的那个类被固定写死在代码中,如果要进行抽象替换便只能修改源码,违背了开闭原则。
用上述例子来说,我们需要设计成将依赖的字典对象dictionary 对象传入拼写检查器SpellChecker中,最简单的方法就是通过SpellChecker的构造函数set进去即可。如下面代码显示:
public class SpellChecker{
private final Lexicon dictionary;
public SpellChecker(Lexicon dictionary){
this.dictionary = dictionary;
}
public boolean isValid(String word){
}
public List<String> suggestions(String typo){
}
}
大家也许看出来了,这就是我们常用的setter getter方法,大家一直都忽略了它依赖注入这样的专有称呼。
这种模式的另一个变体是,将资源工厂传给构造器,工厂是可以被重复调用来创建类的实例的一个对象,在Java8中增加的接口Supplier,最适合用于表示工厂,带有Supplier的方法,通常应该限制输入工厂的类型参数,使用有限制的通配符类型,以便来创建指定限定类型的任意子类型:例如
class SpellChecker {
private final Lexicon dictionary;
// public SpellChecker(Lexicon dictionary){
// this.dictionary = dictionary;
// }
public SpellChecker(Supplier<? extends Lexicon> dictionary) {
this.dictionary = dictionary.get();
}
}
interface Lexicon {
}
class ChineseLexicon implements Lexicon {
}
main:
public class DependencyInjection {
public static void main(String[] args) {
SpellChecker spellChecker = new SpellChecker(ChineseLexicon::new);
}
}
在不适用java8的写法之下我们可以直接采取抽象类的方式,传入抽象类的具体对象注入使用类中即可,适用类中完全面向抽象类变成即可。
//拼写检查器
class SpellChecker {
private final Lexicon dictionary;
public SpellChecker(Lexicon dictionary){
this.dictionary = dictionary;
}
}
//抽象词典类
abstract class Lexicon {
}
//具体词典类
class ChineseLexicon implements Lexicon {
}
main:
public class DependencyInjection {
public static void main(String[] args) {
ChineseLexicon chineseLexicon =new ChineseLexicon()
SpellChecker spellChecker = new SpellChecker(chineseLexicon );
}
}
这里的写法极其类似工厂模式了。
虽然依赖注入极大的提升了灵活性和可测试性,但是如果使用过度,会导致凌乱不堪,因为可能包含上千个依赖,不过这种凌乱,用一个依赖注入框架就可以终结,如Spring,Dagger,Guice等。但是设计成手动依赖注入的API,一般都适用于这些框架。
总而言之,不要用Singleton和静态工具类来实现依赖注入一个或者多个底层资源,且该资源的行为会影响到该类的行为,也不要直接用这个类来创建资源,而是将这些资源或者工厂传递给构造器,通过它来创建资源类,这种实践被称为依赖注入,它极大的提高了类的灵活性、可重用性何可测试性。
参考文章:https://www.jianshu.com/p/ce29dad76099