Effective Java刷書筆記---靜態(tài)工廠方法
類實例獲取--“考慮”用靜態(tài)工廠方法代替構(gòu)造器
對于一個類而言箱硕,為了讓調(diào)用者獲取它自身的一個實例冠桃,最常用的方法就是提供一個公有的構(gòu)造器,比如:
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)工廠方法有名字。
- 構(gòu)造函數(shù)的方式 需要名字和類名相同缸榄,當(dāng)有多個重載渤弛,參數(shù)類型、返回值不同等多種情況下(比如Date函數(shù)重載)甚带,對于使用者來說可能閱讀要查閱每個參數(shù)的意義了才能不調(diào)用錯誤的構(gòu)造器她肯。
- 靜態(tài)工廠方法可以使用不同的方法名字使得其構(gòu)造的對象更加明晰。我們完全可以通過方法名明白構(gòu)造了什么樣的對象鹰贵,幫助客戶端能更好的準(zhǔn)確的調(diào)用正確的實例晴氨。
- 對于構(gòu)造函數(shù)來說,只有參數(shù)有差異(類型碉输、數(shù)量或者順序)才能夠重載
- 靜態(tài)工廠方法允許我們有相同的參數(shù)類型籽前,只要名字不同即可。
即如果構(gòu)造器的參數(shù)本身沒有確切的描述正被返回的對象,那么具有適當(dāng)名稱的靜態(tài)工廠會更容易使用枝哄,代碼更容易閱讀肄梨。
2.不用每次被調(diào)用時都創(chuàng)建新對象。
這一點可能比較好理解挠锥,這樣做避免創(chuàng)建不必要的對象众羡。
有時候外部調(diào)用者只需要拿到一個實例,而不關(guān)心是否是新的實例蓖租;又或者我們想對外提供一個單例時 粱侣。可以用靜態(tài)工廠方式為重復(fù)的調(diào)用返回相同的對象菜秦。
然后順便重溫一下單例: 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.可以返回原返回類型的子類。
這一點符合設(shè)計模式中的基本的原則之一——『里氏替換』原則球昨,就是說子類應(yīng)該能替換父類尔店。構(gòu)造方法只能返回確切的自身類型,而靜態(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)建帶泛型的實例時该肴,能使代碼變得簡潔
這條主要是針對帶泛型類的繁瑣聲明而說的,構(gòu)造器實例方式需要我們重復(fù)書寫兩次泛型參數(shù):
但如果我們通過靜態(tài)工廠方式藐不,編譯器可以幫我們找到類型參數(shù)(類型推導(dǎo))匀哄,只寫一次泛型參數(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.避免寫很多重復(fù)的代碼挑秉,集中管理法梯,統(tǒng)一修改
這一點在業(yè)務(wù)中很常見,比如android開發(fā)中我們需要在MainActivity 中跳到DetailActivity中犀概,最簡單我們會這樣寫
String url="";
Intent intent = new Intent(MainActivity.this,DetailActivity.class);
intent.putExtra("url", url);
context.startActivity(intent);
但當(dāng)其他Activity 也要跳到DetailActivity中立哑,我們還是會重復(fù)的寫這種代碼,以至于我們某天需要統(tǒng)一查看都哪些跳轉(zhuǎn)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再調(diào)用時只需DetailActivity.jumpToDetail(context,url);即可若皱,這樣做省去寫重復(fù)的代碼镊叁,也統(tǒng)一管理參數(shù)尘颓,便于查看調(diào)用者。
靜態(tài)工廠方法的劣勢
-
類如果不含公有的或受保護的構(gòu)造器晦譬,就不能被實例化疤苹。
如果我們在類中將構(gòu)造函數(shù)設(shè)為private,只提供靜態(tài)工廠方法來構(gòu)建對象敛腌,那么我們將不能通過繼承擴展該類卧土。
但是這也會鼓勵我們使用復(fù)合而不是繼承來擴展類。
-
一般構(gòu)造器在API文檔會被明確標(biāo)識出來像樊,方便閱讀查看尤莺。而靜態(tài)工廠方法我們主要是靠命名規(guī)范來彌補這一劣勢。