Effective.Java 讀書筆記(1)靜態工廠和構造方法

1.Consider static factory method instead of constructor
 大意爲考慮使用靜態的工廠方法而不是構造器

用戶在獲得類它本身的實例的時候,通常會想到的就是使用public的構造器,但是一個類可以提供一個public的工廠方法。
這種工廠方法簡化了返回該類實例的靜態方法

文章給出了一個例子

public static Boolean valueOf(boolean b) {
     return b ? Boolean.TRUE : Boolean.FALSE;
}


需要注意的是,這裏的工廠方法並不是設計模式裏面所講的工廠方法
這種工廠方法有優缺點,先說說優點

首先是工廠方法和構造器不一樣,它們擁有名字
如果構造方法沒有一堆參數,那麼有着不同名字的工廠方法更加容易去返回相應描述的對象同時也使得代碼更加容易閱讀

舉個例子,構造方法BigInteger(int, int, Random),返回一個概率性的BigInteger,我們可以用一個工廠方法 BigInteger.probablePrime代替掉,而且提高了可讀性

一個類在給定的標誌(參數列表)下擁有唯一一個構造方法
我們可以通過Java的方法重載實現多個不同參數列表的構造方法,這是實在不是一個好的主意

用記住參數列表的方法來記住不同的構造方法,最後只會因爲使用錯的參數列表而引起錯誤,參數列表太難去記憶了

而且當別人閱讀你的代碼的時候並不知道你的構造器的不同,除非他去翻看你的類文檔

那麼工廠方法擁有自己的名字,靜態的工廠方法就沒有我們前面所說的限制了

所以,當有需要多種構造方法的時候,去創建靜態工廠方法,然後用特殊名字來區分它們是一種更加優的策略

第二個優點是,當這些靜態工廠方法被使用的時候,並不需要創建一個新的對象

這意味着我們可以重複使用之前的構造出來的對象,重複去使用這些實例,避免了沒有必要的對象的產生

之前我們所舉得例子Boolean.valueOf(boolean)方法表現了這個特性,它沒有創建對象,這種特性類似於(Flyweight pattern享元模式),那麼當我們需要重複使用某個相等的對象的時候,並且調用起來可能代價很大的情況下,我們使用工廠模式可以得到表現上很大的提升

使用靜態工廠方法,可以對這些你所需要的實例有着嚴格的控制,不會造成資源浪費等問題,這被稱爲“實例控制”,這樣的實例控制使得一個類可以是單例模式(Singleton)或者 非實例化模式(noninstantiable),當然,也可以是一個一成不變的類(Immutable class),保證了不可能同時存在兩個相等的實例,用代碼來解釋的話就是說, a.equals(b)等價於  當且僅當a==b,也就是你使用“==”來判斷兩個對象是否相等在工廠方法模式下是可行,Enum(枚舉)的類型就提供了這個保證

第三個優點是,靜態工廠方法可以返回任意它們返回類型的子類型(非子類而是子類型,類似數據類型和子數據類型的意思)的對象,這在選擇返回對象的類方面會給你更強大的靈活性

一個有着這樣靈活性的應用就是一個可以返回對象並且不用使他們的類是public的API

隱藏了複雜的API中的實現類,這種特性將自身化爲一種接口爲基礎的框架(interfa-based frameworks),這裏的接口提供了靜態工廠方法自然的返回類型,接口是不能有靜態方法的,故Type接口的靜態方法被放在一個非實例化模式(noninstantiable)類裏面,叫做Types

舉個例子,Java裏面的Collections Framework有着32個collection接口的便利的實現,提供unmodifiable collections, synchronized collections等等,所有的這些實現幾乎都是利用在非實例化類(java.util.Collections)中的靜態工廠方法,返回的對象的類都是非public的

如果這個Collection Framework API的實現都用了分離開來的public類,那樣其體積會大得多

使用這樣的靜態工廠方法呢使得用戶利用接口而不是實現化的類來引用返回對象

當然,使用靜態工廠方法不僅僅能夠使得返回的類是非public的,還能根據靜態工廠的參數不同來使調用不同,任意聲明瞭返回類型的子類型的類是被允許的,這樣增強了軟件的穩定性和表現

Java中的EnumSet,在1.5版本中,沒有構造器,只有許多的靜態工廠,它們返回兩,種實現的一種,取決於enum類型的大小,如果是64或者更少的元素,正如大多數情況下的枚舉,靜態工廠會返回一個RegularEnumSet的實例,受一個簡單的long類型支持,如果超過64了,就返回一個JumboEnumSet的實例,收一個long的array類型支持

第四個優點是,使用靜態工廠方法可以減少創建參數化類型的實例的贅冗,不幸運的是,你必須確定類型參數當你調用參數化類的構造器即使這些參數類型從上下文來看是明顯的,這特別地需要你去提供兩次參數類型才能成功,舉個例子

Map<String, List<String>> m = new HashMap<String, List<String>>();

這樣的類型參數一增加看上去又長又複雜,使用靜態工廠,編譯器可以幫你整理那些參數,這就是我們所知的類型推斷(Type Inference),我們可以使用靜態工廠在HashMap上,假設HashMap提供下面這個靜態工廠

public static <K, V> HashMap<K, V> newInstance() {
     return new HashMap<K, V>();
}

然後創建起來就簡單了許多

Map<String, List<String>> m = HashMap.newInstance();

目前來看Java還沒有進行工廠的加入,可能有一天這種參數推斷會和在方法調用一樣在構造器上表現良好

遺憾的是,1.6版本標準的Collection implementation像HashMap一樣並沒有工廠方法,你可以自己創建工具類來實現,更加重要的是,你可以自己提供自己的參數化類的靜態工廠

我們來說說它的缺點,最主要的缺點就是這樣的一個提供靜態工廠的沒有public或者protected的構造方法的類並不能被子類化,非public並且被一個public的靜態工廠所返回的類也有一樣的情況

舉個例子,我們不可能去子類化任意的擁有便利的實現的類在Collection Framework裏面,可以說這能夠是僞裝起來的一個好事,正如它鼓勵程序員去使用組合而不是繼

第二個缺點是,靜態工廠方法不容易和其他的靜態方法區分,它們並沒有在API文檔中如同構造器一樣,所以可能去解決怎樣去初始化一個提供靜態工廠方法而不是構造器的類的時候會有點困難,javadoc工具可能有一天可以關注一下這些靜態工廠的方法,爲了減少這種區分上的問題,我們可以自己堅持通用命名轉換。

比如valueOf,of,getInstance,newInstance,getType

總結,靜態工廠方法和public構造器都有它們的用處,都有它們的優缺點,通常來說靜態工廠更推薦,所以需要避免本能的提供public構造方法沒不是優先考慮一下靜態工廠

發佈了45 篇原創文章 · 獲贊 12 · 訪問量 4萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章