Effective Java 第三版读书笔记——条款5:使用依赖注入替代替代硬连接资源

许多类都会依赖一个或多个基本资源。例如,拼写检查器依赖于字典。下面是两种错误的实现方式:

  • 使用 static utility classes:

    // 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) { ... }
    }
  • 使用 singletons:

    // 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),每个实例都使用客户端所期望的资源(即示例中的 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) { ... }
}

依赖注入模式非常简单。 虽然示例中的拼写检查器只有一个资源(字典),但是依赖注入可以使用任意数量的资源和任意的依赖图。 它保持了不变性(条款 17),因此多个客户端可以共享依赖对象(假设客户需要相同的底层资源)。 依赖注入同样适用于构造方法,静态工厂(条款 1)和 builder模式(条款 2)。

尽管依赖注入极大地提高了灵活性和可测试性,但它可能使大型项目变得混乱,这些项目通常包含数千个依赖项。使用依赖注入框架(如 Dagger、Guice 或 Spring)可以消除这些混乱。

猜你喜欢

转载自www.cnblogs.com/LeeFire/p/9936148.html