美文网首页
第五条:优先考虑依赖注入来引入资源

第五条:优先考虑依赖注入来引入资源

作者: Js_Gavin | 来源:发表于2020-12-01 20:30 被阅读0次

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

    public class SpellChecker{
       private static final Lexicon dictionary = ...;
    
       private SpellChecker(){
       }
    
       public static boolean isValid(String word){
       }
    
       public static List<String> suggestions(String typo){
       }
    }
    

    同样的,将这些类实现为Singleton的做法也并不少见

    public class SpellChecker{
       private static final Lexicon dictionary = ...;
    
       private SpellChecker(){
       }
       
       public static final INSTANCE = new SpellChecker();
       
       public static boolean isValid(String word){
       }
    
       public static List<String> suggestions(String typo){
       }
    }
    

    以上两种方式都不理想,因为它们都假定为只有一本词典,实际上,每种语言都有自己不同的词典,特殊语言还有特殊的词典等等。因此假定用一本词典来满足所有需求,是不现实的。所以静态工厂和Singleton不适合用于需要引用底层资源的类。

    这里需要的是能支持类的多个实例,每一个实例都使用客户端指定资源。满足需求最简单的模式是:当创建一个新的实例时,就将资源传入到构造器,这就是依赖注入的一种形式:词典是拼音检查器的一个依赖,在创建拼音检查器时就将词典注入到其中。

    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){
       }
    }
    

    这种模式的另一个变体是,将资源工厂传给构造器,工厂是可以被重复调用来创建类的实例的一个对象,在Java8中增加的接口Supplier<T>,最适合用于表示工厂,带有Supplier<T>的方法,通常应该限制输入工厂的类型参数,使用有限制的通配符类型,以便来创建指定限定类型的任意子类型:例如

    public class DependencyInjection {
    
        public static void main(String[] args) {
            SpellChecker spellChecker = new SpellChecker(ChineseLexicon::new);
        }
    
    }
    
    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 {
    
    }
    
    public class DependencyInjection {
        public static void main(String[] args) {
            SpellChecker spellChecker = new SpellChecker(ChineseLexicon::new);
        }
    }
    

    虽然依赖注入极大的提升了灵活性和可测试性,但是如果使用过度,会导致凌乱不堪,因为可能包含上千个依赖,不过这种凌乱,用一个依赖注入框架就可以终结,如Spring,Dagger,Guice等。但是设计成手动依赖注入的API,一般都适用于这些框架。

    总而言之,不要用Singleton和静态工具类来实现依赖注入一个或者多个底层资源,且该资源的行为会影响到该类的行为,也不要直接用这个类来创建资源,而是将这些资源或者工厂传递给构造器,通过它来创建资源类,这种实践被称为依赖注入,它极大的提高了类的灵活性、可重用性何可测试性。

    相关文章

      网友评论

          本文标题:第五条:优先考虑依赖注入来引入资源

          本文链接:https://www.haomeiwen.com/subject/umhkwktx.html