Effective Java刷書筆記---靜態(tài)工廠方法
類實例獲取--“考慮”用靜態(tài)工廠方法代替構造器
對于一個類而言,為了讓調用者獲取它自身的一個實例,最常用的方法就是提供一個公有的構造器,比如:
Fragment fragment = new MyFragment();
Date date = new Date();
byte[] buf = new byte[2048];
File dir = new File(path);
而我們也要在日常開發(fā)中“考慮”靜態(tài)工廠方法,靜態(tài)工廠方法也是獲取這個類自身的一個實例,他的存在是為了更好的描述和處理這個類。比如:
Calendar calendar = Calendar.getInstance();
Boolean b=Boolean.valueOf(xxx);
Calendar.java
public static Calendar getInstance()
{
return createCalendar(TimeZone.getDefault(), Locale.getDefault(Locale.Category.FORMAT));
}
Boolean.java
public static Boolean valueOf(boolean b) {
return (b ? TRUE : FALSE);
}
靜態(tài)工廠方法的優(yōu)勢:
1.靜態(tài)工廠方法有名字。
- 構造函數(shù)的方式 需要名字和類名相同,當有多個重載,參數(shù)類型、返回值不同等多種情況下(比如Date函數(shù)重載),對于使用者來說可能閱讀要查閱每個參數(shù)的意義了才能不調用錯誤的構造器。
- 靜態(tài)工廠方法可以使用不同的方法名字使得其構造的對象更加明晰。我們完全可以通過方法名明白構造了什么樣的對象,幫助客戶端能更好的準確的調用正確的實例。
- 對于構造函數(shù)來說,只有參數(shù)有差異(類型、數(shù)量或者順序)才能夠重載
- 靜態(tài)工廠方法允許我們有相同的參數(shù)類型,只要名字不同即可。
即如果構造器的參數(shù)本身沒有確切的描述正被返回的對象,那么具有適當名稱的靜態(tài)工廠會更容易使用,代碼更容易閱讀。
2.不用每次被調用時都創(chuàng)建新對象。
這一點可能比較好理解,這樣做避免創(chuàng)建不必要的對象。
有時候外部調用者只需要拿到一個實例,而不關心是否是新的實例;又或者我們想對外提供一個單例時 ??梢杂渺o態(tài)工廠方式為重復的調用返回相同的對象。
然后順便重溫一下單例: Hi,我們再來聊一聊Java的單例吧
public class Single {
private static Single instance;
private Single() {}
public static Single getInstance() {
if (instance == null) {
synchronized (Single.class) {
if (instance == null) {
instance = new Single();
}
}
}
return instance;
}
}
3.可以返回原返回類型的子類。
這一點符合設計模式中的基本的原則之一——『里氏替換』原則,就是說子類應該能替換父類。構造方法只能返回確切的自身類型,而靜態(tài)工廠方法則能夠更加靈活,可以根據(jù)需要方便地返回任何它的子類型的實例。
比如我們現(xiàn)在想根據(jù)消息類型返回不同的消息子類(比如文本消息、圖片消息、視頻消息等等)
public static Message getMessage(TIMMessage message){
switch (message.getElement(0).getType()){
case Text:
case Face:
return new TextMessage(message);
case Image:
return new ImageMessage(message);
case Sound:
return new VoiceMessage(message);
case Video:
return new VideoMessage(message);
case GroupTips:
return new GroupTipMessage(message);
case File:
return new FileMessage(message);
case Custom:
return new CustomMessage(message);
case UGC:
return new UGCMessage(message);
default:
return null;
}
}
4.在創(chuàng)建帶泛型的實例時,能使代碼變得簡潔
這條主要是針對帶泛型類的繁瑣聲明而說的,構造器實例方式需要我們重復書寫兩次泛型參數(shù):
但如果我們通過靜態(tài)工廠方式,編譯器可以幫我們找到類型參數(shù)(類型推導),只寫一次泛型參數(shù)。
//常規(guī)實例化方式
Map<String, List<String>> m =
new HashMap<String, List<String>>();
public static <K, V> HashMap<K, V> newInstance() {
return new HashMap<K, V>();
}
//使用靜態(tài)工廠方法實例化,簡化繁瑣的聲明
Map<String, List<String>> m = HashMap.newInstance();
但是jdk1.7之后做了優(yōu)化,不用靜態(tài)方法也可以只寫一次類型參數(shù)了。
5.避免寫很多重復的代碼,集中管理,統(tǒng)一修改
這一點在業(yè)務中很常見,比如android開發(fā)中我們需要在MainActivity 中跳到DetailActivity中,最簡單我們會這樣寫
String url="";
Intent intent = new Intent(MainActivity.this,DetailActivity.class);
intent.putExtra("url", url);
context.startActivity(intent);
但當其他Activity 也要跳到DetailActivity中,我們還是會重復的寫這種代碼,以至于我們某天需要統(tǒng)一查看都哪些跳轉DetailActivity時我們需要全局搜索嗎??或者修改intent傳入?yún)?shù)時,也不方便查找。所以此時我們使用靜態(tài)工廠方式,在DetailActivity中加入這個一個方法:
public static void jumpToDetail(Context context, String url){
Intent intent = new Intent(context, DetailActivity.class);
intent.putExtra("url", url);
context.startActivity(intent);
}
此時其他activity再調用時只需DetailActivity.jumpToDetail(context,url);即可,這樣做省去寫重復的代碼,也統(tǒng)一管理參數(shù),便于查看調用者。
靜態(tài)工廠方法的劣勢
-
類如果不含公有的或受保護的構造器,就不能被實例化。
如果我們在類中將構造函數(shù)設為private,只提供靜態(tài)工廠方法來構建對象,那么我們將不能通過繼承擴展該類。
但是這也會鼓勵我們使用復合而不是繼承來擴展類。
-
一般構造器在API文檔會被明確標識出來,方便閱讀查看。而靜態(tài)工廠方法我們主要是靠命名規(guī)范來彌補這一劣勢。