Effective Java刷書筆記---靜態(tài)工廠方法
類實(shí)例獲取--“考慮”用靜態(tài)工廠方法代替構(gòu)造器
對(duì)于一個(gè)類而言,為了讓調(diào)用者獲取它自身的一個(gè)實(shí)例,最常用的方法就是提供一個(gè)公有的構(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)工廠方法也是獲取這個(gè)類自身的一個(gè)實(shí)例,他的存在是為了更好的描述和處理這個(gè)類。比如:
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)勢(shì):
1.靜態(tài)工廠方法有名字。
- 構(gòu)造函數(shù)的方式 需要名字和類名相同,當(dāng)有多個(gè)重載,參數(shù)類型、返回值不同等多種情況下(比如Date函數(shù)重載),對(duì)于使用者來說可能閱讀要查閱每個(gè)參數(shù)的意義了才能不調(diào)用錯(cuò)誤的構(gòu)造器。
- 靜態(tài)工廠方法可以使用不同的方法名字使得其構(gòu)造的對(duì)象更加明晰。我們完全可以通過方法名明白構(gòu)造了什么樣的對(duì)象,幫助客戶端能更好的準(zhǔn)確的調(diào)用正確的實(shí)例。
- 對(duì)于構(gòu)造函數(shù)來說,只有參數(shù)有差異(類型、數(shù)量或者順序)才能夠重載
- 靜態(tài)工廠方法允許我們有相同的參數(shù)類型,只要名字不同即可。
即如果構(gòu)造器的參數(shù)本身沒有確切的描述正被返回的對(duì)象,那么具有適當(dāng)名稱的靜態(tài)工廠會(huì)更容易使用,代碼更容易閱讀。
2.不用每次被調(diào)用時(shí)都創(chuàng)建新對(duì)象。
這一點(diǎn)可能比較好理解,這樣做避免創(chuàng)建不必要的對(duì)象。
有時(shí)候外部調(diào)用者只需要拿到一個(gè)實(shí)例,而不關(guān)心是否是新的實(shí)例;又或者我們想對(duì)外提供一個(gè)單例時(shí) ??梢杂渺o態(tài)工廠方式為重復(fù)的調(diào)用返回相同的對(duì)象。
然后順便重溫一下單例: Hi,我們?cè)賮砹囊涣腏ava的單例吧
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.可以返回原返回類型的子類。
這一點(diǎn)符合設(shè)計(jì)模式中的基本的原則之一——『里氏替換』原則,就是說子類應(yīng)該能替換父類。構(gòu)造方法只能返回確切的自身類型,而靜態(tài)工廠方法則能夠更加靈活,可以根據(jù)需要方便地返回任何它的子類型的實(shí)例。
比如我們現(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í)例時(shí),能使代碼變得簡潔
這條主要是針對(duì)帶泛型類的繁瑣聲明而說的,構(gòu)造器實(shí)例方式需要我們重復(fù)書寫兩次泛型參數(shù):
但如果我們通過靜態(tài)工廠方式,編譯器可以幫我們找到類型參數(shù)(類型推導(dǎo)),只寫一次泛型參數(shù)。
//常規(guī)實(shí)例化方式
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)工廠方法實(shí)例化,簡化繁瑣的聲明
Map<String, List<String>> m = HashMap.newInstance();
但是jdk1.7之后做了優(yōu)化,不用靜態(tài)方法也可以只寫一次類型參數(shù)了。
5.避免寫很多重復(fù)的代碼,集中管理,統(tǒng)一修改
這一點(diǎn)在業(yè)務(wù)中很常見,比如android開發(fā)中我們需要在MainActivity 中跳到DetailActivity中,最簡單我們會(huì)這樣寫
String url="";
Intent intent = new Intent(MainActivity.this,DetailActivity.class);
intent.putExtra("url", url);
context.startActivity(intent);
但當(dāng)其他Activity 也要跳到DetailActivity中,我們還是會(huì)重復(fù)的寫這種代碼,以至于我們某天需要統(tǒng)一查看都哪些跳轉(zhuǎn)DetailActivity時(shí)我們需要全局搜索嗎??或者修改intent傳入?yún)?shù)時(shí),也不方便查找。所以此時(shí)我們使用靜態(tài)工廠方式,在DetailActivity中加入這個(gè)一個(gè)方法:
public static void jumpToDetail(Context context, String url){
Intent intent = new Intent(context, DetailActivity.class);
intent.putExtra("url", url);
context.startActivity(intent);
}
此時(shí)其他activity再調(diào)用時(shí)只需DetailActivity.jumpToDetail(context,url);即可,這樣做省去寫重復(fù)的代碼,也統(tǒng)一管理參數(shù),便于查看調(diào)用者。
靜態(tài)工廠方法的劣勢(shì)
-
類如果不含公有的或受保護(hù)的構(gòu)造器,就不能被實(shí)例化。
如果我們?cè)陬愔袑?gòu)造函數(shù)設(shè)為private,只提供靜態(tài)工廠方法來構(gòu)建對(duì)象,那么我們將不能通過繼承擴(kuò)展該類。
但是這也會(huì)鼓勵(lì)我們使用復(fù)合而不是繼承來擴(kuò)展類。
-
一般構(gòu)造器在API文檔會(huì)被明確標(biāo)識(shí)出來,方便閱讀查看。而靜態(tài)工廠方法我們主要是靠命名規(guī)范來彌補(bǔ)這一劣勢(shì)。