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