Effective Java刷書筆記---靜態工廠方法
Effective Java刷書筆記---靜態工廠方法
類例項獲取--“考慮”用靜態工廠方法代替構造器
對於一個類而言,為了讓呼叫者獲取它自身的一個例項,最常用的方法就是提供一個公有的構造器,比如:
Fragment fragment = new MyFragment(); Date date = new Date(); byte[] buf = new byte[2048]; File dir = new File(path);
而我們也要在日常開發中“考慮”靜態工廠方法,靜態工廠方法也是獲取這個類自身的一個例項,他的存在是為了更好的描述和處理這個類。比如:
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); }
靜態工廠方法的優勢:
1.靜態工廠方法有名字。
- 建構函式的方式 需要名字和類名相同,當有多個過載,引數型別、返回值不同等多種情況下(比如Date函式過載),對於使用者來說可能閱讀要查閱每個引數的意義了才能不呼叫錯誤的構造器。
- 靜態工廠方法可以使用不同的方法名字使得其構造的物件更加明晰。我們完全可以通過方法名明白構造了什麼樣的物件,幫助客戶端能更好的準確的呼叫正確的例項。
- 對於建構函式來說,只有引數有差異(型別、數量或者順序)才能夠過載
-
靜態工廠方法允許我們有相同的引數型別,只要名字不同即可。
即如果構造器的引數本身沒有確切的描述正被返回的物件,那麼具有適當名稱的靜態工廠會更容易使用,程式碼更容易閱讀。
2.不用每次被呼叫時都建立新物件。
這一點可能比較好理解,這樣做避免建立不必要的物件。
有時候外部呼叫者只需要拿到一個例項,而不關心是否是新的例項;又或者我們想對外提供一個單例時 。可以用靜態工廠方式為重複的呼叫返回相同的物件。
然後順便重溫一下單例:ofollow,noindex">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.可以返回原返回型別的子類。
這一點符合設計模式中的基本的原則之一——『里氏替換』原則,就是說子類應該能替換父類。構造方法只能返回確切的自身型別,而靜態工廠方法則能夠更加靈活,可以根據需要方便地返回任何它的子型別的例項。
比如我們現在想根據訊息型別返回不同的訊息子類(比如文字訊息、圖片訊息、視訊訊息等等)
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.在建立帶泛型的例項時,能使程式碼變得簡潔
這條主要是針對帶泛型類的繁瑣宣告而說的,構造器例項方式需要我們重複書寫兩次泛型引數:
但如果我們通過靜態工廠方式,編譯器可以幫我們找到型別引數(型別推導),只寫一次泛型引數。
//常規例項化方式 Map<String, List<String>> m = new HashMap<String, List<String>>(); public static <K, V> HashMap<K, V> newInstance() { return new HashMap<K, V>(); } //使用靜態工廠方法例項化,簡化繁瑣的宣告 Map<String, List<String>> m = HashMap.newInstance(); 但是jdk1.7之後做了優化,不用靜態方法也可以只寫一次型別引數了。
5.避免寫很多重複的程式碼,集中管理,統一修改
這一點在業務中很常見,比如android開發中我們需要在MainActivity 中跳到DetailActivity中,最簡單我們會這樣寫
String url=""; Intent intent = new Intent(MainActivity.this,DetailActivity.class); intent.putExtra("url", url); context.startActivity(intent);
但當其他Activity 也要跳到DetailActivity中,我們還是會重複的寫這種程式碼,以至於我們某天需要統一檢視都哪些跳轉DetailActivity時我們需要全域性搜尋嗎??或者修改intent傳入引數時,也不方便查詢。所以此時我們使用靜態工廠方式,在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);即可,這樣做省去寫重複的程式碼,也統一管理引數,便於檢視呼叫者。
靜態工廠方法的劣勢
-
類如果不含公有的或受保護的構造器,就不能被例項化。
如果我們在類中將建構函式設為private,只提供靜態工廠方法來構建物件,那麼我們將不能通過繼承擴充套件該類。
但是這也會鼓勵我們使用複合而不是繼承來擴充套件類。
-
一般構造器在API文件會被明確標識出來,方便閱讀檢視。而靜態工廠方法我們主要是靠命名規範來彌補這一劣勢。