【第5条】优先考虑依赖注入来引用资源

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

 

 

许多类依赖于一个或多个底层资源。例如,拼写检查器依赖于字典。将此类类实现为静态工具类并不少见: 

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

同样地,将它们实现为单例的做法也并不少见:

 

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

这两种方法都不令人满意,因为他们都是假设只有一本字典可用。实际上,每种语言都有自己的字典,特殊的字典被用于特殊的词汇表。另外,可能还需要用特殊的词典进行测试。想当然地认为一本字典就足够了,这是一厢情愿的想法。

 

可以通过使 dictionary 属性设置为非 final ,并添加一个方法来更改现有拼写检查器中的字典,从而让 SpellChecker 支持多个字典,但是这样的设置显得非常笨拙、容易出错、并且无法并行工作。静态工具类和单例类不适合与需要引用底层资源的类。

 

这里所需要的是能够支持类的多个实例 (在我们的示例中是指 SpellChecker ),每个实例都使用客户端所期望的资源(在我们的例子中是 dictionary )。满足这一需求的简单模式是,在创建新实例时将资源传递到构造器中。这是依赖项注入(dependency injection)的一种形式:字典是拼写检查器的一个依赖项,当它创建时被注入到拼写检查器中。

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

依赖注入模式非常简单,许多程序员使用它多年而不知道它有一个名字。虽然我们的拼写检查器的例子只有一个资源(字典),但是依赖项注入可以使用任意数量的资源和任意依赖图。它保持了不变性,因此多个客户端可以共享依赖对象(假设客户需要相同的底层资源)。依赖注入同样适用于构造器、静态工厂和 builder 模式。

 

该模式的一个有用的变体是将资源工厂传递给构造器。工厂是可以重复调用以创建类型实例的对象。这种工厂体现了工厂方法模式(Factory Method pattern)。 Java 8 中引入的Supplier<T> 接口非常适合代表工厂。在输入上采用 Supplier<T> 的方法通常应该使用有界的通配符类型(bounded wildcard type)约束工厂的类型参数,以便客户端能够传入一个工厂,来创建指定类型的任意子类型。例如,下面是一个生产马赛克的方法,它利用客户端提供的工厂来生产每一片马赛克:

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

尽管依赖注入极大地提高了灵活性和可测试性,但它可能使大型项目变得混乱,这些项目通常包含数千个依赖项。使用依赖注入框架(如 Dagger [Dagger]、Guice [Guice] 或 Spring [Spring])可以消除这些混乱。这些框架的使用超出了本书的范围,但是请注意,为手动依赖注入而设计的 API 非常适合这些框架的使用。

 

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

 

                                                                         扫描二维码(关注公众号)、获取更多学习资料

                                                             

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值