許多類依賴一個或者多個底層資源,如:拼寫檢查類依賴于一個字典類
// 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 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 suggestions(String typo) { ... }
? }
這兩種寫法都不是令人滿意的,因為這兩種寫法不支持字典類的替換,都假定只提供一種字典類。
缺點如下:
不夠靈活、不可測試
因此
靜態(tài)工具類與單例類都不適用于依賴底層資源的情景
更好的做法是
在創(chuàng)建類實例的時候?qū)⒁蕾囐Y源通過構(gòu)造函數(shù)傳遞進去
這是依賴注入的一種形式:將拼寫檢查類依賴的字典類通過構(gòu)造函數(shù)注入進來
// 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 suggestions(String typo) { ... }
? }
同時,依賴注入還支持多個類依賴同一個底層資源
依賴注入通過構(gòu)造器、靜態(tài)工廠、Builder注入都是等效的
該模式的一種非常實用的變體:向構(gòu)造函數(shù)傳遞一個資源的工廠類
Java8可用Supplier<T>接口來表示此工廠來提供資源的實例
對于使用Supplier<T>作為輸入?yún)?shù)的方法,應(yīng)該提供一個類型限定的參數(shù)作為輸入,允許客戶端傳入一個創(chuàng)建子類的工廠。
Mosaic create(Supplier tileFactory) { ... }
盡管依賴注入極大的提高了靈活性和可測試性,它同時也使大的項目更加雜亂,但是這可以使用依賴注入框架來解決
總之,不要使用單例和工具類來實現(xiàn)一個類依賴另一個底層資源
而應(yīng)該,傳遞資源或者創(chuàng)建資源的工廠到構(gòu)造器(或者工廠方法、builder)
這種實踐將極大的增加類的靈活性、重用性和可測試性