《Effective Java 中文版 第二版》第二章 第1條:考慮用靜態(tài)工廠方法代替構造器

本章的主題是創(chuàng)建和銷毀對象:何時以及如何創(chuàng)建對象,何時以及如何避免創(chuàng)建對象奏候,如何確保它們能夠適時地銷毀循集,以及如何管理對象銷毀之前必須進行的各種清理動作。


注意:靜態(tài)工廠方法與設計模式中的工廠方法模式不同蔗草。本條目中所指的靜態(tài)工廠方法并不直接對應設計模式中的工廠方法咒彤。

[toc]

類可以通過靜態(tài)工廠方法來提供它的客戶端,而不是通過構造器咒精。這么做有以下幾大優(yōu)勢:

  1. 它們有名稱镶柱。
    當一個類需要多個帶有相同簽名的構造器時,就用靜態(tài)工廠方法代替構造器模叙,并且慎重地選擇名稱以便突出它們之間的區(qū)別

  2. 不必每次調用它們的時候都創(chuàng)建一個對象歇拆。
    能夠為重復的調用返回相同的對象,有助于類總能嚴格控制在某個時刻哪些實例應該存在。這種類被稱作實例受控類(instance-controlled)故觅。編寫實例受控類有幾個原因:確保它是一個Singleton(見第三條 單例)或者 是不可實例化的(見第4條)厂庇。還使得不可變的類(見第15條)可以確保不會存在兩個相等的實例,即當且僅當 a==b 的時候才有 a.equals(b) 為 true输吏,可以提升性能【書這里單詞錯了】

  3. 它們可以返回原返回類型的任何子類型的對象权旷。
    這樣我們在選擇返回對象的類時就有了更大的靈活性。這種靈活性的一種應用是贯溅,API 可以返回對象拄氯,同時又不會使對象的類變成公有的。以這種方式隱藏實現類會使API變得非常簡介盗迟。這項技術適用于基于接口的框架(interface-based-framework坤邪,見第18條 ),在這種框架中罚缕,接口為靜態(tài)工廠方法提供了 自然返回類型艇纺。


自然返回類型(natural return types)

可能有人會對自然返回類型產生疑問,以下是加粗文字的英文原版:

where interfaces provide natural return types for static factory methods.

個人的理解是:此框架靜態(tài)工廠方法返回的類型都是接口類型邮弹,也就是接口類型為靜態(tài)工廠方法提供了返回類型黔衡,這點我會結合下面的 java.util.Collections 源碼(JDK 1.8)說明。

在此框架中腌乡,接口為靜態(tài)工廠方法提供了自然返回類型盟劫。接口不能有靜態(tài)方法,按照慣例与纽,接口 Type 的靜態(tài)工廠方法被放在一個名為Types的不可實例化類(見第4條)中侣签。

書中提了一個 Java Collections Framework 的例子,這里我結合 Collections 的源碼急迂,整理一下思路影所。

首先書中說 Java Collections Framework 的便利實現,幾乎都通過靜態(tài)工廠方法在一個不可實例化的類(java.util.Collections)中導出僚碎。所有返回對象的類都是非公有的猴娩。

java.util.Collections 類

構造器私有,不可實例勺阐。

Collections 類中的 unmodifiableMap 方法

返回對象的類為非公有類(靜態(tài)私有)卷中,此框架靜態(tài)工廠方法返回的類型都是接口類型。

比導出獨立公有類的那種實現方式要小得多渊抽,這不僅僅是指API上的減少蟆豫,也是概念上的減少。
用戶 知道懒闷,被返回的對象是由相關的接口精確指定的十减,所以它們不需要閱讀有關的類文檔徙瓶。使用這種靜態(tài)工廠方法時,甚至要求 客戶端 通過制定接口來引用被返回的對象嫉称,而不是通過它的實現類來引用被返回的對象,這是一種良好的習慣(見第52條)【客戶與用戶的差別在第一章有說明】

公有的靜態(tài)工廠方法返回的對象類不僅可以是非公有的灵疮,還可以隨著每次調用時的方法參數不同而變化织阅,只要返回的是已聲明返回類型的子類型,都是允許的震捣,為了提升軟件的可維護性和性能荔棉,返回對象的類也可能隨著發(fā)行版本的不同而不同。

書中提到的 java.util.EnumSet 類蒿赢,沒有構造器(抽象類)润樱,可結合閱讀,截圖(JDK1.8)如下:


java.util.EnumSet 類

客戶端永遠不知道也不關心他們從工廠方法種得到的對象的類;只關系它是所需類的某個子類即可。


服務提供者框架

因為篇幅原因兵多,這里以 JDBC 簡要說明敞嗡,詳細內容可自行 google 。

此框架由四個組件組成挥唠,前三個為重要組件,最后一個為可選組件。

  • 服務接口(Service Interface):提供者(Mysql Oracle)實現的;
  • 提供者注冊 API(Provider Registration API):系統(tǒng)用于注冊實現赂蕴,使客戶端訪問;
  • 服務訪問 API(Service access API):客戶端用來獲取服務實例舶胀;
  • 服務提供者接口(Service Provider Interface):提供者負責創(chuàng)建其服務實現的實例概说;

對照 JDBC

  • 服務接口: Connection
  • 提供者注冊API: DriverManager.registerDriver
  • 服務訪問API: DriverManager.getConnection
  • 服務提供者接口: Driver

其中,Connection 只是一個接口(規(guī)范)嚣伐,各大數據庫廠商【提供者】根據此接口編寫實現類糖赔,讓 DriverManager.registerDriver 注冊,使得客戶端可以使用 DriverManager.getConnection 獲得實例纤控,如果有提供服務提供者接口 Driver 則由其創(chuàng)建服務實現的實例挂捻,如果沒有就按照類名注冊,并通過反射實例化船万。


適配器模式(adapter):

適配器模式的個人理解:實現舊接口刻撒,組合所需類,在舊接口的方法中調用所需類的方法耿导。

此模式除了上述3個優(yōu)點外声怔,還增加了第四個優(yōu)點

在構建參數化類型實例的時候,它們使代碼變得更加簡潔

在調用參數化類的構造器時舱呻,即使類型參數很明顯醋火,也必須指明悠汽。這通常要求你鏈接兩次提供類型的參數:

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

而有了靜態(tài)工廠方法,編譯器舊可以替你找到類型參數芥驳。這被稱為類型推導(type inference)【在泛型章節(jié)也有提到】柿冲。

public static <K , V> HashMap <K , V> newInstance ( ) {

    return new HashMap<K , V> ( ) ;

}

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

總有一天,Java 將能夠在構造器調用以及方法調用中執(zhí)行這種類型推導兆旬,但到發(fā)行版 1.6 為止暫時還不能這么做【JDK 1.8 也沒有】假抄。標準的 Map 實現如 HashMap 并沒有工廠方法,但是可以將這些方法放入你自己的工具箱丽猬。


靜態(tài)工廠方法的主要缺點:

  • 類如果不含公有的或者受保護的構造器宿饱,就不能被子類化
    鼓勵程序員使用復合(composition)脚祟,而不是繼承(見第16條)

  • 它們與其他的靜態(tài)方法 實際上沒有任何區(qū)別谬以。
    在 API 文檔中,它們沒有像構造器那樣被明確的標識出來由桌,你可以通過在 類 或者 接口 注釋中關注靜態(tài)工廠为黎,并遵守標準的命名習慣,也可以彌補這一劣勢沥寥。


靜態(tài)工廠方法的一些慣用名稱:

  • valueOf:不太嚴格的講碍舍,該方法返回的實例與它的參數具有相同的值。這樣的靜態(tài)工廠方法實際上是類型轉換方法邑雅。

  • of:valueOf 的一種更為簡介的替代片橡,在 EnumSet(見第32條) 中使用并流行起來。

  • getInstance:返回的實例是通過方法的參數來描述的淮野,但是不能夠說與參數具有同樣是值捧书。對于 Singleton 【單例】來說,該方法沒有參數骤星,并返回唯一的實例经瓷。

  • newInstance:像 getInstance 一樣,但 newInstance 能夠確保返回的每個實例都與所有其他不同洞难。

  • getType:像 getInstance 一樣舆吮,但是在工廠方法處于不同的類中的時候使用。Type 標識工廠方法返回的類型對象【getInteger队贱、getLong】色冀。

  • newType:像 newInstance 一樣,但是在工廠方法處于不同的類中的時候使用柱嫌。Type 標識工廠方法返回的類型對象【newInteger锋恬、newLong】。

簡而言之编丘,靜態(tài)工廠方法 和 公有構造器 各有用處与学,我們需要理解它們各自的長處彤悔。靜態(tài)工廠通常更加合適,因此切記第一反應就是提供公有的構造器索守,而不先考慮靜態(tài)工廠晕窑。

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市卵佛,隨后出現的幾起案子幕屹,更是在濱河造成了極大的恐慌,老刑警劉巖级遭,帶你破解...
    沈念sama閱讀 222,183評論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現場離奇詭異渺尘,居然都是意外死亡挫鸽,警方通過查閱死者的電腦和手機,發(fā)現死者居然都...
    沈念sama閱讀 94,850評論 3 399
  • 文/潘曉璐 我一進店門鸥跟,熙熙樓的掌柜王于貴愁眉苦臉地迎上來丢郊,“玉大人,你說我怎么就攤上這事医咨》阖遥” “怎么了?”我有些...
    開封第一講書人閱讀 168,766評論 0 361
  • 文/不壞的土叔 我叫張陵拟淮,是天一觀的道長干茉。 經常有香客問我,道長很泊,這世上最難降的妖魔是什么角虫? 我笑而不...
    開封第一講書人閱讀 59,854評論 1 299
  • 正文 為了忘掉前任,我火速辦了婚禮委造,結果婚禮上戳鹅,老公的妹妹穿的比我還像新娘。我一直安慰自己昏兆,他們只是感情好枫虏,可當我...
    茶點故事閱讀 68,871評論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著爬虱,像睡著了一般隶债。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上饮潦,一...
    開封第一講書人閱讀 52,457評論 1 311
  • 那天燃异,我揣著相機與錄音,去河邊找鬼继蜡。 笑死回俐,一個胖子當著我的面吹牛逛腿,可吹牛的內容都是我干的。 我是一名探鬼主播仅颇,決...
    沈念sama閱讀 40,999評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼单默,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了忘瓦?” 一聲冷哼從身側響起搁廓,我...
    開封第一講書人閱讀 39,914評論 0 277
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎耕皮,沒想到半個月后境蜕,有當地人在樹林里發(fā)現了一具尸體,經...
    沈念sama閱讀 46,465評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡凌停,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 38,543評論 3 342
  • 正文 我和宋清朗相戀三年粱年,在試婚紗的時候發(fā)現自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片罚拟。...
    茶點故事閱讀 40,675評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡台诗,死狀恐怖,靈堂內的尸體忽然破棺而出赐俗,到底是詐尸還是另有隱情拉队,我是刑警寧澤,帶...
    沈念sama閱讀 36,354評論 5 351
  • 正文 年R本政府宣布阻逮,位于F島的核電站粱快,受9級特大地震影響,放射性物質發(fā)生泄漏叔扼。R本人自食惡果不足惜皆尔,卻給世界環(huán)境...
    茶點故事閱讀 42,029評論 3 335
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望币励。 院中可真熱鬧慷蠕,春花似錦、人聲如沸食呻。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,514評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽仅胞。三九已至每辟,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間干旧,已是汗流浹背渠欺。 一陣腳步聲響...
    開封第一講書人閱讀 33,616評論 1 274
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留椎眯,地道東北人挠将。 一個月前我還...
    沈念sama閱讀 49,091評論 3 378
  • 正文 我出身青樓胳岂,卻偏偏與公主長得像,于是被迫代替她去往敵國和親舔稀。 傳聞我的和親對象是個殘疾皇子乳丰,可洞房花燭夜當晚...
    茶點故事閱讀 45,685評論 2 360

推薦閱讀更多精彩內容