Effective Java 的笔记,代码、英语原文为主,批注、翻译为辅。

Item 5: Prefer dependency injection to hardwiring resources

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

Condition

Many classes depend on one or more underlying resources. For example, a spell checker depends on a dictionary.

Inappropriate use of static utility - inflexible & untestable!

1
2
3
4
5
6
7
// 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!

1
2
3
4
5
6
7
8
// 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) { ... }
}

Why Inappropriate?

Neither of these approaches is satisfactory, because they assume that there is only one dictionary worth using. In practice, each language has its own dictionary, and special dictionaries are used for special vocabularies. Also, it may be desirable to use a special dictionary for testing. It is wishful thinking to assume that a single dictionary will suffice for all time.

// wishful:这边应该可以理解成“痴心妄想”。

Static utility classes and singletons are inappropriate for classes whose behavior is parameterized by an underlying resource.

Dependency injection provides flexibility and testability

What is required is the ability to support multiple instances of the class (in our example, SpellChecker), each of which uses the resource desired by the client (in our example, the dictionary). A simple pattern that satisfies this requirement is to pass the resource into the constructor when creating a new instance. This is one form of dependency injection: the dictionary is a dependency of the spell checker and is injected into the spell checker when it is created.

the dependency of the spell checker(dictionary) is injected into the spell checker when it is created.

1
2
3
4
5
6
7
8
9
// 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) { ... }
}

Summary

In summary, do not use a singleton or static utility class to implement a class that depends on one or more underlying resources whose behavior affects that of the class, and do not have the class create these resources directly. Instead, pass the resources, or factories to create them, into the constructor (or static factory or builder). This practice, known as dependency injection, will greatly enhance the flexibility, reusability, and testability of a class.