本文轉載自:https://www.jianshu.com/p/ceb5ec8f1174
-
序:什麼是靜态工廠方法
- 2.1 靜态工廠方法與構造器不同的第一優勢在于,它們有名字
- 2.2 第二個優勢,不用每次被調用時都建立新對象
- 2.3 第三個優勢,可以傳回原傳回類型的子類
- 2.4 第四個優勢,在建立帶泛型的執行個體時,能使代碼變得簡潔
1. 序:什麼是靜态工廠方法
在 Java 中,獲得一個類執行個體最簡單的方法就是使用
new
關鍵字,通過構造函數來實作對象的建立。
就像這樣:
Fragment fragment = new MyFragment();
// or
Date date = new Date();
不過在實際的開發中,我們經常還會見到另外一種擷取類執行個體的方法:
Fragment fragment = MyFragment.newIntance();
// or
Calendar calendar = Calendar.getInstance();
// or
Integer number = Integer.valueOf("3");
↑ 像這樣的:不通過
new
,而是用一個靜态方法來對外提供自身執行個體的方法,即為我們所說的
靜态工廠方法(Static factory method)
。
知識點:
new
究竟做了什麼?
簡單來說:當我們使用 new 來構造一個新的類執行個體時,其實是告訴了 JVM 我需要一個新的執行個體。JVM 就會自動在記憶體中開辟一片空間,然後調用構造函數來初始化成員變量,最終把引用傳回給調用方。
2. Effective Java
在關于 Java 中書籍中,《Effective Java》絕對是最負盛名幾本的之一,在此書中,作者總結了幾十條改善 Java 程式設計的金玉良言。其中開篇第一條就是『考慮使用靜态工廠方法代替構造器』,關于其原因,作者總結了 4 條(第二版),我們先來逐個看一下。
2.1 靜态工廠方法與構造器不同的第一優勢在于,它們有名字
由于語言的特性,Java 的構造函數都是跟類名一樣的。這導緻的一個問題是構造函數的名稱不夠靈活,經常不能準确地描述傳回值,在有多個重載的構造函數時尤甚,如果參數類型、數目又比較相似的話,那更是很容易出錯。
比如,如下的一段代碼 :
Date date0 = new Date();
Date date1 = new Date(0L);
Date date2 = new Date("0");
Date date3 = new Date(1,2,1);
Date date4 = new Date(1,2,1,1,1);
Date date5 = new Date(1,2,1,1,1,1);
—— Date 類有很多重載函數,對于開發者來說,假如不是特别熟悉的話,恐怕是需要猶豫一下,才能找到合适的構造函數的。而對于其他的代碼閱讀者來說,估計更是需要檢視文檔,才能明白每個參數的含義了。
(當然,Date 類在目前的 Java 版本中,隻保留了一個無參和一個有參的構造函數,其他的都已經标記為 @Deprecated 了)
而如果使用靜态工廠方法,就可以給方法起更多有意義的名字,比如前面的
valueOf
、
newInstance
、
getInstance
等,對于代碼的編寫和閱讀都能夠更清晰。
2.2 第二個優勢,不用每次被調用時都建立新對象
這個很容易了解了,有時候外部調用者隻需要拿到一個執行個體,而不關心是否是新的執行個體;又或者我們想對外提供一個單例時 —— 如果使用工廠方法,就可以很容易的在内部控制,防止建立不必要的對象,減少開銷。
在實際的場景中,單例的寫法也大都是用靜态工廠方法來實作的。
如果你想對單例有更多了解,可以看一下這裡:☞《Hi,我們再來聊一聊Java的單例吧》
2.3 第三個優勢,可以傳回原傳回類型的子類
這條不用多說,設計模式中的基本的原則之一——『裡氏替換』原則,就是說子類應該能替換父類。
顯然,構造方法隻能傳回确切的自身類型,而靜态工廠方法則能夠更加靈活,可以根據需要友善地傳回任何它的子類型的執行個體。
Class Person {
public static Person getInstance(){
return new Person();
// 這裡可以改為 return new Player() / Cooker()
}
}
Class Player extends Person{
}
Class Cooker extends Person{
}
比如上面這段代碼,Person 類的靜态工廠方法可以傳回 Person 的執行個體,也可以根據需要傳回它的子類 Player 或者 Cooker。(當然,這隻是為了示範,在實際的項目中,一個類是不應該依賴于它的子類的。但如果這裡的 getInstance () 方法位于其他的類中,就更具有的實際操作意義了)
2.4 第四個優勢,在建立帶泛型的執行個體時,能使代碼變得簡潔
這條主要是針對帶泛型類的繁瑣聲明而說的,需要重複書寫兩次泛型參數:
Map<String,Date> map = new HashMap<String,Date>();
不過自從 java7 開始,這種方式已經被優化過了 —— 對于一個已知類型的變量進行指派時,由于泛型參數是可以被推導出,是以可以在建立執行個體時省略掉泛型參數。
Map<String,Date> map = new HashMap<>();
是以這個問題實際上已經不存在了。