Tips
《Effective Java, Third Edition》一書英文版已經出版,這本書的第二版想必不少人都讀過,號稱Java四大名著之一,不過第二版2009年出版,到如今已經將近8年的時間,但隨着Java 6,7,8,甚至9的發佈,Java語言發生了深入的變化。
在這裏第一時間翻譯成中文版。供你們學習分享之用。程序員
許多類依賴於一個或多個底層資源。例如,拼寫檢查器依賴於字典。將此類類實現爲靜態實用工具類並很多見(條目 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<String> suggestions(String typo) { ... } }
一樣地,將它們實現爲單例也並很多見(條目 3):app
// 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
,並添加一個方法來更改現有拼寫檢查器中的字典,從而讓拼寫檢查器支持多個字典,可是在併發環境中,這是笨拙的、容易出錯的和不可行的。靜態實用類和單例對於那些行爲被底層資源參數化的類來講是不合適的。ide
所須要的是可以支持類的多個實例(在咱們的示例中,即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)。學習
該模式的一個有用的變體是將資源工廠傳遞給構造方法。 工廠是能夠重複調用以建立類型實例的對象。 這種工廠體現了工廠方法模式(Factory Method pattern )[Gamma95]。 Java 8中引入的Supplier <T>
接口很是適合表明工廠。 在輸入上採用Supplier<T>
的方法一般應該使用有界的通配符類型( bounded wildcard type)(條目 31)約束工廠的類型參數,以容許客戶端傳入工廠,建立指定類型的任何子類型。 例如,下面是一個使用客戶端提供的工廠生成tile的方法:
Mosaic create(Supplier<? extends Tile> tileFactory) { ... }
測試
儘管依賴注入極大地提升了靈活性和可測試性,但它可能使大型項目變得混亂,這些項目一般包含數千個依賴項。使用依賴注入框架(如Dagger[Dagger]、Guice[Guice]或Spring[Spring])能夠消除這些混亂。這些框架的使用超出了本書的範圍,可是請注意,爲手動依賴注入而設計的API很是適合這些框架的使用。flex
總之,不要使用單例或靜態的實用類來實現一個類,該類依賴於一個或多個底層資源,這些資源的行爲會影響類的行爲,而且不讓類直接建立這些資源。相反,將資源或工廠傳遞給構造方法(或靜態工廠或builder模式)。這種稱爲依賴注入的實踐將極大地加強類的靈活性、可重用性和可測試性。ui