創(chuàng)建和銷毀對象
重點關(guān)注對象的創(chuàng)建和銷毀:什么時候、如何創(chuàng)建對象,什么時候、什么條件下應(yīng)該避免創(chuàng)建對象,如何保證對象在合適的方式下被銷毀,如何在銷毀對象之前操作一些必須的清理行為。
嘗試用靜態(tài)工廠方法代替構(gòu)造器
如果一個 client 要實例化一個對象來使用,傻 b 都知道應(yīng)該先調(diào)用類的構(gòu)造器來 new 一個對象,之后再調(diào)用相應(yīng)的方法。除了這個方式, Java Effective 還建議了另一種方法:用靜態(tài)工廠方法來提供一個類的實例。以下的例子不反映兩者的優(yōu)劣,只是反映兩者在代碼實現(xiàn)上的不同,優(yōu)劣之后再談:
假設(shè)咱們要一個顏色為黑色、長度為
Hammer myHammer =? new Hammer(Color.BLACK, 50);
而用靜態(tài)工廠方法來實例化一個對象,如下
Hammer myHammer = Hammer.factory(Color.BLACK,50);
也可以用專門的一個工廠類來實例化
Hammer myHammer = Toolkit.factory(“Hammer”, Color.BLACK,50);?
單純從上面的代碼上看,真的只有傻 b 才會選擇靜態(tài)工廠的方法,完全就是多此一舉,直接 new 又快又爽,搞這么麻煩做莫斯(武漢話“什么”的意思)?
別急,別急,你急個莫 b (武漢粗話:基本就是“你急個毛”的意思)?
下面就說說用靜態(tài)工廠代替構(gòu)造器的好處( advantage )和不好處( disadvantage )。
第一個好處,講你都不信,行家們認(rèn)為,構(gòu)造器有一個不好的地方就是:這個方法的簽名(
signture
)太固定了。
構(gòu)造器的名字是固定的,生個 Hammer ,構(gòu)造器的名字就是 Hammer (……),唯一能變化的地方就是參數(shù),假設(shè)我的這個錘子有兩個很變態(tài)的構(gòu)造需要:
1 :第一個參數(shù)是顏色( Color 型),第二個參數(shù)是錘子頭的重量( int 型)。
Hammer ( Color c, int kg ) {
//remainder omited
}
2 :第一個參數(shù)是顏色( Color 型),第二個參數(shù)是錘子的長度( int 型)。
Hammer ( Color c, int cm ) {
//remainder omited
}
感覺滿足需要了,但是細(xì)心一看,完了,構(gòu)造器的參數(shù)列表類型重復(fù)了,肯定編譯通不過,這是面向?qū)ο髽?gòu)造器天生的缺陷——唯一的變化就是參數(shù),參數(shù)都分辨不了,就真的分辨不了。
而另外就算參數(shù)能分辨的了,構(gòu)造器一多,它的參數(shù)一多,您根本就不知道每個參數(shù)是用來干什么的,只能去查閱文檔,在您已經(jīng)眼花繚亂的時候再去查文檔,一個一個的對,折磨人的活。
這個時候,您就可以考慮用靜態(tài)工廠方法來實例化對象了。因為靜態(tài)工廠方法有一個最簡單的特點就是:他有可以變化的方法名(構(gòu)造器的名字變不了)。用名字的不同來代表不同的構(gòu)造需要,這么簡單的普通的特點在這里就是它相對于構(gòu)造器的 advantage 。
如上面的錘子的例子可以這樣:
1 : Hammer.produceByWeight (Color c, int kg){
//remainder omited
}
2 : Hammer.produceByHeight (Color c, int cm){
//remainder omited
}
這是不是一目了然多了。嗯,我是這樣認(rèn)為的。
第二個好處,“靜態(tài)工廠方法不需要每次都真的去實例化一個對象”——其實這也是另一些優(yōu)化方法的前提。
構(gòu)造器的每次 invoke 必定會產(chǎn)生一個新的對象,而靜態(tài)工廠方法經(jīng)過一定的控制,完全可以不用每次 invoke 都生成一個新的對象。
為什么不每次都生成一個對象的原因就不必說了,因為原因太明顯。這個原因就是為什么要“共享”對象的原因。
下面講講通常使用的兩種共享具體策略,也就是具體方法了:
1 :單例模式的需要,一旦需要某個對象有單例的需要,必定對于這類對象的構(gòu)造只能用靜態(tài)工廠方法了。
2 : flyweight 模式和不變( immutable ) 模式的需要,這兩個模式很多時候都說一起使用的,一旦一些對象我們認(rèn)為是不變的,那自然就想拿來重用,也就說共享,而 flyweight 就是用來重用這些小粒度對象的。
如 Boolean.valueOf (boolean) 方法:
Boolean a = Boolean.valueOf (100);
Boolean b = Boolean.valueOf (100);
?a,??b兩個引用都是指向同一個對象。
這些對象都是不變的,而 valueOf 的控制就是用的 flyweight 方法。
這種一個狀態(tài)(如上面一個數(shù)字)對應(yīng)的對象只有一個還有一個好處,就是可以直接通過比較“引用”來判斷他們是否
equel
(這里的
equel
是邏輯相等的意思),以前需要
a.equels(b)
,而一旦用“
flyweight
模式和不變(
immutable
)
模式”后,避免了產(chǎn)生多余的相同對象,用
a==b
就可以達(dá)到
a.equels(b)
的目的了。這樣當(dāng)然優(yōu)化了
performance
。?
第三個好處,其實就是工廠方法的核心好處——我把它稱為“抽象類型構(gòu)造器”。它可以為我們提供一個抽象類型的實例,同時必要的隱藏了抽象類型的具體結(jié)構(gòu)。這是
new
怎么都達(dá)不到的。
這種模式的好處其實就是面向?qū)ο蟮淖詈诵牡暮锰帲橄蠛途唧w可以分離,一旦抽象定義好了,具體的東西可以慢慢的變化,慢慢的拓展——開閉原則。
如
Collections Framework API
,都是描述集合類型的接口,也就是對于客戶端來看,只有
Collection
這個類要認(rèn)識,而實際上,實現(xiàn)這個接口的
Collection
是多種多樣的。如果要讓用戶都知道這些具體實現(xiàn)的
Collection
,就增加了復(fù)雜度。
這時,通過一個靜態(tài)工廠方法,就可以隱藏各種
Collection
的具體實現(xiàn),而讓
Client
只使用返回的
Collection
對象就可以了。
這里還可以加上一些權(quán)限控制,如這些實現(xiàn)只要對于工廠來講是可以訪問的,不用是
public
的,而他們只要通過
public
的工廠就可以提供給用戶。非常有利于代碼的安全。
靜態(tài)工廠方法的第一個缺點就是:使用靜態(tài)工廠方法創(chuàng)建的類的構(gòu)造器經(jīng)常都是非公共或非
protected
的。
這樣,以后這些類就沒有辦法被繼承了。不過也有人說,不用繼承就用
composition
唄。也是!呵呵。
靜態(tài)工廠方法的第二個缺點是:在
jdk
文檔里,這些靜態(tài)工廠方法很難跟別的靜態(tài)方法相區(qū)別。
而文檔中,構(gòu)造器是很容易看到的。
為了一定程度解決這個問題,我們可以用一些比較特別的名字來給這類靜態(tài)工廠方法來命名。最常用的有:
valueOf
——
用來放回跟參數(shù)“相同值”的對象。
getInstance
——
返回一個對象的實例。單例模式中,就是返回單例對象。
總結(jié):靜態(tài)工廠方法和構(gòu)造器都有各自的特點。最好在考慮用構(gòu)造器之前能先考慮一下靜態(tài)工廠方法,往往,后者更有用一點。如果權(quán)衡了以后也看不出那個好用一些,那就用構(gòu)造器,畢竟簡單本分多了。