Effective Java 第5条 优先考虑依赖注入来引入资源

优先考虑依赖注入来引入资源

有许多类会依赖一个或者多个底层的资源,例如:拼写检查器需要依赖词典。因此,像下面这种把类实现为静态工具类的做法很常见。

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

猜你喜欢

转载自blog.csdn.net/qq_38941937/article/details/115311981