技術公眾號:Java In Mind(Java_In_Mind),歡迎關注孽查!
前言
我相信大部分都看過《Effective Java》這本Java神書,這本書在我學習Java的路上給了我?guī)椭梢哉f是受益匪淺,書中第一篇就建議:考慮用靜態(tài)工廠方法代替構造器狼牺。我自從看了之后就開始使用實踐該建議,到現(xiàn)在已經(jīng)基本偏愛靜態(tài)工廠方法,這里我就談談使用以來的一些領悟症杏。
使用靜態(tài)工廠方法的優(yōu)缺點
優(yōu)點
- 靜態(tài)工廠方法有名稱
- 不必每次調(diào)用都創(chuàng)建一個新對象
- 可以返回原類型的任何子類型的對象
- 創(chuàng)建參數(shù)化類型實例的時候,它們使代碼更加簡潔
缺點
- 類如果不含公有的或者受保護的構造器瑞信,就不能被子類化
- 靜態(tài)工廠方法與其他靜態(tài)方法沒有任何區(qū)別
靜態(tài)工廠方法有名稱
這點確實是很好地優(yōu)勢厉颤,靜態(tài)工廠方法有自己的名稱,也就意味著可以表達某種意思凡简,或者某些特殊的實例逼友,例如,在使用一些通用返回結果的時候秤涩,可以用類似如此的靜態(tài)工廠方法來創(chuàng)建實例:
public class Result {
private boolean success;
private String message;
private Object content;
private Result(boolean success,String message,Object content){
this.success = success;
this.message = message;
this.content = content;
}
public static Result ofSuccess(Object content){
return new Result(true,"處理成功",content);
}
public static Result ofFail(String message){
return new Result(false,message,null);
}
// getter and setter...
}
//使用
Result.ofSuccess(content);
Result.ofFail("處理失敗帜乞,找不到該記錄");
這樣子,通過靜態(tài)工廠方法就可以很好表達要構造的對象的目的筐眷,而調(diào)用方可以比較方便獲取實例黎烈。
不必每次調(diào)用都創(chuàng)建一個新對象
這個其實很常見,如Boolean類中的幾個valueOf
靜態(tài)工廠方法匀谣,Boolean有兩個靜態(tài)域:TRUE
和FALSE
照棋,這樣就不用每次創(chuàng)建一個新對象:
public static Boolean valueOf(boolean b) {
return (b ? TRUE : FALSE);
}
其實,我們在使用單例的時候武翎,通常獲取單例的方法就是一個靜態(tài)工廠方法烈炭,并且也是不用創(chuàng)建一個新的對象:
public class SingleTon {
private static SingleTon singleTon = new SingleTon();
private SingleTon() {
}
public static SingleTon getInstance() {
return singleTon;
}
}
可以返回原類型的任何子類型的對象
靜態(tài)工廠方法返回的實例可以是該類型的子類,而這個子類可以在編寫靜態(tài)工廠方法時不存在宝恶,這也就意味著可以由其他方來實現(xiàn)符隙,也意味著對拓展友好,還可以根據(jù)靜態(tài)工廠方法的入?yún)磉x擇不同的實現(xiàn)卑惜,非常靈活膏执。
在JDK中有個例子就是
EnumSet
驻售,根據(jù)入?yún)⒌拿杜e的枚舉值量來決定使用哪種枚舉實例露久。在使用JDBC的時候,
DriveManager
在獲取連接前得先加載對應的實現(xiàn)驅動欺栗,這樣就能把實現(xiàn)交給第三方來完成毫痕,自己只需提供上層的抽象框架-
日常編程中可以用于同一需求的不同實現(xiàn)征峦,通過靜態(tài)工廠方法選擇實現(xiàn),并且有利于后期拓展消请,例如事件處理:
public abstract class EventHandler { private static Map<String,EventHandler> handlers = new HashMap<>(); //事件類型 public abstract String type(); //子類注冊入口 public static void register(EventHandler eh){ handlers.put(eh.type(),eh); } //獲取不同的實現(xiàn) public static EventHandler getHandler(Event e){ return handlers.get(e.getType()) } }
創(chuàng)建參數(shù)化類型實例的時候栏笆,它們使代碼更加簡潔
靜態(tài)工廠方法還有一個好處就是提高代碼的簡潔度,也使得代碼更加易讀:
-
使用JDK的類型推斷臊泰,不過這在現(xiàn)在的JDK已經(jīng)可以直接推斷了
public class Maps { public static <K, V> HashMap<K, V> newHashMap() { return new HashMap<K, V>(); } } //使用靜態(tài)工廠方法 Map<String,Object> map = Maps.newHashMap(); //使用構造方法 Map<String,Object> map = new HashMap<>();//JDK1.6之前不支持這種寫法
正如第一點的例子蛉加,靜態(tài)工廠方法可以有自己的名稱,可以有一些默認值設置缸逃,可以減少構建實例的參數(shù)针饥,自然也會提高代碼的簡潔度
類如果不含公有的或者受保護的構造器,就不能被子類化
類不含有public或者protect的構造方法時需频,子類的構建需要使用到父類的構造器丁眼,這個時候靜態(tài)工廠方法就不適合
靜態(tài)工廠方法與其他靜態(tài)方法沒有任何區(qū)別
使用靜態(tài)工廠方法的時候,如果該類包含其他的靜態(tài)方法昭殉,那么這個時候對于開發(fā)編程是不友好的苞七,特別是在方法比較多,命名比較混亂的時候挪丢,使用者不能一眼就知道該用哪個方法來構造自己想要的實例蹂风,所以在編寫靜態(tài)工廠方法的時候盡量使用大家習慣的、約定俗成的命名風格:
- of
- valueOf
- getInstance / newInstance
- getType / newType吃靠,其中Type為類型硫眨,如,EventHandler.getEventHandler()
總結
結合我個人的經(jīng)驗巢块,我認為使用靜態(tài)工廠方法的利大于弊:
- 提高代碼的可閱讀性
- 更加符合人類的正常思維方式礁阁,想要某個類的實例直接找某個類就能獲取到,而不是通過new關鍵字
- 能夠根據(jù)不同的需求創(chuàng)建不同的實例族奢,而不用讓看代碼的人推測new出來的對象然后set某些值去猜測某種意圖姥闭,通常我們的做法是注釋,但是使用靜態(tài)工廠的方法命名不是更加簡潔嗎
- 提高代碼的拓展性 越走,方便拓展棚品,對修改友好,符合開閉原則