? ? ? 許多類都會依賴一個或者多個潛在的資源狸吞,例如一個拼寫檢查依賴一個字典蜡豹。很容易就可以看見一個使用這樣實現(xiàn)的靜態(tài)工具類。(條目4)
//??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) { ... }
}
?相似的,很容易看到他們作為單例來實現(xiàn)(條目3):
// 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)工廠玄帕,因為他們都假定只有一個字典類可以被使用附井。實際上讨越,每一種語言都有他們的字典两残,同時特定的字典通常被使用到特地的詞匯上。所以把跨,使用一個特定的字典來測試的設(shè)計可能并不令人滿意人弓,而思考假定一個字典能夠滿足所有的需求是明智的!
? ? ?你可能會嘗試讓SpellChecker 的dictionary 這個字段不是final的着逐,同時添加一個方法來在當(dāng)前存在的所有dictionary 的范圍內(nèi)改變一個dictionary 這個字段崔赌,這樣可以使SpellChecker?支持多個字典。但是將會讓人很尷尬耸别,又很容易讓人因為使用了錯誤的設(shè)置而出錯健芭。靜態(tài)工具類和單例模式出現(xiàn)在一個參數(shù)會因為潛在的資源發(fā)生變化的類中是不恰當(dāng)?shù)摹?/p>
? ? ?現(xiàn)在的需求是,能夠去支持這個類的多個實例秀姐,每一個都會使用到客戶端的一些資源(在這個例子中慈迈,是我們的dictionary),一個滿足這樣需求的簡單的模式是當(dāng)創(chuàng)建一個新的實例的時候?qū)⑦@些資源傳入構(gòu)造器中省有,這形成了依賴注入:這個dictionary是SpellChecker 的一個依賴痒留,然后我們在SpellChecker?將dictionary注入到對象中去。
? ??// 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) { ...?}
}
? ? ?這個依賴注入模式非常簡單蠢沿,以至于很多程序員使用了很多年都不知道它有一個名字伸头。但是我們的拼寫例子只有一個資源的依賴,依賴注入?yún)s是可以在任意數(shù)量的資源和任意規(guī)模的依賴圖中工作搏予!它保證了不能改變的特性熊锭,所以多個客戶端能夠共享這些依賴對象(假設(shè)這些客戶端都潛在地期望著相同的資源)。依賴注入和構(gòu)造器雪侥,靜態(tài)工廠(條目1)碗殷,和建造者模式(條目2)相等。
? 這個模式一個非常有用的變體是速缨,通過一個資源工廠將資源注入到構(gòu)造器中锌妻,一個工廠是一個可以重復(fù)調(diào)用的可以創(chuàng)建出一個實例的對象,這樣的工廠表現(xiàn)為工廠方法模式旬牲。這個Supplier<T>接口被添加到j(luò)ava8中仿粹,它可以很好地去提供一個工廠。使用通配符的泛型特性原茅,方法的輸入典型地強(qiáng)迫指定工廠參數(shù)類型來允許工廠可以創(chuàng)建一個指定類型的子類型吭历。例如這里有一個方法,客戶端使用提供的可以創(chuàng)建任意類型的工廠創(chuàng)建mosaic擂橘。
? ? ? 盡管依賴注入很好地提高了靈活性和可測試性晌区,但是他可能會使一個包含成千上萬的依賴的大型工程變得散亂。不過這種散亂可以被依賴注入框架所排出,諸如:Dagger,Guice,Spring朗若。這些框架的使用超過了這本書的范圍恼五,但是記住那些由這些框架為了依賴注入而設(shè)計出來的細(xì)致的API是非常容易使用的。
? ?總結(jié):不使用單例模式或者靜態(tài)工具類來實現(xiàn)一個依賴超過一種隱含資源依賴的類哭懈,這些隱含資源的表現(xiàn)會影響整個類的效果灾馒!同時這些資源的在類的之前被創(chuàng)建。取而代之的是遣总,通過將這些資源和工廠傳入構(gòu)造器睬罗。實際上,依賴注入將會更好地提高一個類的靈活性彤避,復(fù)用性傅物,和可測試性!