Effective Java笔记(5)优先考虑依赖注入来引用资源

        有许多类会依赖一个或多个底层的资源 。 例如,拼写检查器需要依赖词典 。 因此,像下面这样把类实现为静态工具类的做法并不少见(详见第 4 条):

// Inappropriate use of static utility - inflexible & untestable!
public class SpellChecker {
    private static final Lexicon dictionary = ...;
    private SpellChecker() {} // Noninstantiable
    public static boolean isValid(String word) { ... }
    public static List<String> suggestions(String typo) { ... }
}

        同样地,将这些类实现为 Singleton 的做法也并不少见(详见第 3 条) :

// Inappropriate use of singleton - inflexible & untestable!
public class SpellChecker {
    private final Lexicon dictionary = ...;
    private SpellChecker(...) {}
    public static INSTANCE = new SpellChecker(...);
    public boolean isValid(String word) { ... }
    public List<String> suggestions(String typo) { ... }
}

        以上两种方法都不理想,因为它们都是假定只有一本词典可用 。 实际上,每一种语言都有自己的词典,特殊同汇还要使用特殊的词典 。 此外,可能还需要用特殊的词典进行测试 。 因此假定用一本词典,就能满足所有需求,这简直是痴心妄想 。

        建议尝试用 SpellChecker 来支持多词典,即在现有的拼写检查器中,设 dictionary域为 nonfinal ,并添加一个方法用它来修改词典,但是这样的设置会显得很笨拙、容易出错,并且无法并行工作 。 静态工具类和 Singleton 类不适合于需要引用底层资源的类 。

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

// Dependency injection provides flexibility and testability
public class SpellChecker {
    private final Lexicon dictionary;
    public SpellChecker(Lexicon dictionary) {
        this.dictionary = objects.requireNonNull(dictionary);
    }
    public boolean isValid(String word) { ... }
    public List<String> suggestions(String typo) { ... }
}

        依赖注入模式就是这么简单,因此许多程序员使用多年,却不知道它还有名字呢 。 虽然这个拼写检查器的范例中只有一个资源(词典),但是依赖注入却适用于任意数量的资源,以及任意的依赖形式 。 依赖注入的对象资源具有不可变性,因此多个客户端可以共享依赖对象(假设客户端们想要的是同一个底层资惊)。依赖注入也同样适用于构造器、静态工厂和构建器 。

        这个程序模式的另一种有用的变体是,将资源工厂( factory )传给构造器 。 工厂是可以被重复调用来创建类型实例的一个对象 。 这类工厂具体表现为工厂方法( Factory Method) 。 在 Java 8 中增加的接口 SupplierT>,最适合用于表示工厂 。带有 Supplier<T>的方法,通常应该限制输入工厂的类型参数使用有限制的通配符类型( bounded wildcard type ),以便客户端能自多传入一个工厂,来创建指定类型的任意子类型 。 例如,下面是一个生产马赛克的方法,它利用客户端提供的工厂来生产每一片马赛克 :

Mosaic create(Supplier<? extends Tile> tileFactory) { ... }

        虽然依赖注入极大地提升了灵活性和可测试性,但它会导致大型项目凌乱不堪,因为它通常包含上千个依赖 。 不过这种凌乱用一个依赖、注入框架( dep endency injection framework )便可以终结,如 Dagger 、Guice或者 Spring。 

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值