靜態(tài)工廠方法代替構(gòu)造器_第1頁
靜態(tài)工廠方法代替構(gòu)造器_第2頁
靜態(tài)工廠方法代替構(gòu)造器_第3頁
靜態(tài)工廠方法代替構(gòu)造器_第4頁
靜態(tài)工廠方法代替構(gòu)造器_第5頁
全文預(yù)覽已結(jié)束

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡介

1、第2章 創(chuàng)建和銷毀對(duì)象本章的主題是創(chuàng)建和銷毀對(duì)象:何時(shí)以及如何創(chuàng)建對(duì)象,何時(shí)以及如何避免創(chuàng)建對(duì)象,如何確保它們能夠被適時(shí)地銷毀,以及如何管理銷毀之前必須進(jìn)行的所有清除動(dòng)作。第1條:考慮用靜態(tài)工廠方法代替構(gòu)造器對(duì)于類而言,為了讓客戶端獲取它自身的一個(gè)實(shí)例,最常用的方法就是提供一個(gè)公有的構(gòu)造器。還有一種方法,也應(yīng)該成為每個(gè)程序員的工具箱中的一部分。類可以提供一個(gè)公有的靜態(tài)工廠方法(static factory method),它只是一個(gè)返回類的實(shí)例的靜態(tài)方法。下面是一個(gè)來自Boolean(基本類型boolean的包裝類)的簡單示例。這個(gè)方法將boolean基本類型值轉(zhuǎn)換成了一個(gè)Boolean對(duì)象引

2、用:public static Boolean valueOf(boolean b) return b ? Boolean.TRUE : Boolean.FALSE;注意,靜態(tài)工廠方法與設(shè)計(jì)模式(Design Patterns)Gamma95, p.107中的工廠方法(Factory Method)模式不同。本條目中所指的靜態(tài)工廠方法并不直接對(duì)應(yīng)于設(shè)計(jì)模式中的工廠方法。類可以通過靜態(tài)工廠方法來提供它的客戶端,而不是通過構(gòu)造器。提供靜態(tài)工廠方法而不是公有的構(gòu)造器具有兩大優(yōu)勢。靜態(tài)工廠方法與構(gòu)造器不同的第一大優(yōu)勢在于,它們有名稱。如果構(gòu)造器的參數(shù)本身沒有確切地描述正被返回的對(duì)象,那么具有適當(dāng)名稱的

3、靜態(tài)工廠會(huì)更容易使用,產(chǎn)生的客戶端代碼也更易于閱讀。例如,構(gòu)造器BigInteger(int, int, Random)返回的BigInteger可能為素?cái)?shù)(prime),如果用名為BigIbablePrime的靜態(tài)工廠方法來表示,顯然要好一些。(1.4的發(fā)行版本中最終增加了這個(gè)方法。)一個(gè)類只能有一個(gè)帶有指定簽名的構(gòu)造器。編程人員通常知道如何避開這一限制:通過提供兩個(gè)構(gòu)造器,它們的參數(shù)列表只在參數(shù)類型的順序上有所不同。實(shí)際上這并不是個(gè)好主意。這種API的用戶永遠(yuǎn)也不會(huì)記得哪個(gè)構(gòu)造器是哪個(gè),最終會(huì)導(dǎo)致調(diào)用錯(cuò)誤的構(gòu)造器。人們讀到使用了這些構(gòu)造器的代碼時(shí),如果沒有參考類的文檔,

4、往往不知道這段代碼是做什么用的。由于靜態(tài)工廠方法有名稱,所以它們沒有像前一段落中所談到那種限制。如果一個(gè)類可能需要多個(gè)帶有相同簽名的構(gòu)造器時(shí),就用靜態(tài)工廠方法代替構(gòu)造器,并且慎重地選擇名稱以便突出它們之間的區(qū)別。靜態(tài)工廠方法與構(gòu)造器不同的第二大優(yōu)勢在于,不必在每次調(diào)用它們的時(shí)候都創(chuàng)建一個(gè)新對(duì)象。這使得不可變的類(見第15條)可以使用預(yù)先構(gòu)建好的實(shí)例,或者將構(gòu)建好的實(shí)例緩存起來,進(jìn)行重復(fù)分發(fā),以避免重復(fù)創(chuàng)建不必要的對(duì)象。Boolean.valueOf(boolean)方法示范了這種方法:它從來不創(chuàng)建對(duì)象。這種方法類似于Flyweight模式Gamma95,p.195。如果程序經(jīng)常請(qǐng)求相同的對(duì)象,

5、尤其當(dāng)創(chuàng)建對(duì)象的成本很高時(shí),這種方法可以極大地提升性能。靜態(tài)工廠方法能夠?yàn)橹貜?fù)的調(diào)用返回相同對(duì)象,這樣有助于類總能嚴(yán)格控制在某個(gè)時(shí)刻哪些實(shí)例應(yīng)該存在。這種類被稱作實(shí)例受控的類(instance-controlled)。編寫實(shí)例受控的類有幾個(gè)原因。實(shí)例受控使得類可以確保它是一個(gè)Singleton(見第3條)或者是不可實(shí)例化的(見第4條)。它還使得不可變的類(見第15條)可以確保不會(huì)存在兩個(gè)相等的實(shí)例,即當(dāng)且僅當(dāng)a=b的時(shí)候才有a.equals(b)。如果類保證了這一點(diǎn),它的客戶端就可以使用=操作符來代替equals(Object)方法,這樣可以提升性能。枚舉(enum)類型(第30條)保證了這一

6、點(diǎn)。靜態(tài)工廠方法與構(gòu)造器不同的第三大優(yōu)勢在于,它們可以返回原返回類型的任何子類型的對(duì)象。這樣我們在選擇返回對(duì)象的類時(shí)就有了很大的靈活性。這種靈活性的一種應(yīng)用是,API可以返回對(duì)象,同時(shí)又不會(huì)使它們的類變成公有的。以這種方式隱藏實(shí)現(xiàn)類會(huì)使API變得非常簡潔。這種方法適合于基于接口的框架(interface-based framework,見第18條),因?yàn)樵谶@種框架中,接口為靜態(tài)工廠方法提供了自然返回類型。接口不能有靜態(tài)方法,因此按照慣例,接口Type的靜態(tài)工廠方法被放在一個(gè)名為Types的不可實(shí)例化的類(見第4條)中?,F(xiàn)在的Collections Framework API比導(dǎo)出32個(gè)獨(dú)立公有

7、類的那種實(shí)現(xiàn)方式要小得多,每種便利實(shí)現(xiàn)對(duì)應(yīng)一個(gè)類。這不僅僅是指API數(shù)量上的減少,也是概念意義上的減少。用戶知道,被返回的對(duì)象是由相關(guān)的接口精確指定的,所以他們不需要閱讀該實(shí)現(xiàn)類的額外類文檔。使用這種靜態(tài)工廠方法時(shí),甚至要求客戶端通過接口來引用被返回的對(duì)象,而不是通過它的實(shí)現(xiàn)類來引用被返回的對(duì)象,一般來說這是一種良好的習(xí)慣(見第52條)。公有的靜態(tài)工廠方法所返回的對(duì)象的類不僅可以是非公有的,而且該類還可以隨著每次調(diào)用而發(fā)生變化,具體取決于靜態(tài)工廠方法的參數(shù)值。只要是已聲明的返回類型的子類型,都是允許的。為了提升軟件的可維護(hù)性和性能,返回對(duì)象的類也可能隨著不同的發(fā)行版本而不同。這兩個(gè)實(shí)現(xiàn)類的存在

8、對(duì)于客戶端來說是不可見的。如果RegularEnumSet不能再給小的枚舉類型提供性能優(yōu)勢,就可以從未來的發(fā)行版本中將它刪除,不會(huì)造成不良的影響。同樣地,如果事實(shí)證明對(duì)性能有好處,也可能在未來的發(fā)行版本中添加第三甚至第四個(gè)EnumSet實(shí)現(xiàn)??蛻舳擞肋h(yuǎn)不知道也不關(guān)心他們從工廠方法中得到的對(duì)象的類;他們只關(guān)心它是EnumSet的某個(gè)子類即可。靜態(tài)工廠方法返回的對(duì)象所屬的類,在編寫包含該靜態(tài)工廠方法的類時(shí)可以不必存在。這種靈活的靜態(tài)工廠方法構(gòu)成了服務(wù)提供者框架(Service Provider Framework)的基礎(chǔ),例如JDBC(Java數(shù)據(jù)庫連接, Java Database Connec

9、tivity)API。服務(wù)提供者框架是指這樣一個(gè)系統(tǒng):多個(gè)服務(wù)提供者實(shí)現(xiàn)一個(gè)服務(wù),系統(tǒng)為服務(wù)提供者的客戶端提供多個(gè)實(shí)現(xiàn),并把他們從多個(gè)實(shí)現(xiàn)中解耦出來。服務(wù)提供者框架中有三個(gè)重要的組件:服務(wù)接口(Service Interface),這是提供者實(shí)現(xiàn)的;提供者注冊API(Provider Registration API),這是系統(tǒng)用來注冊實(shí)現(xiàn),讓客戶端訪問它們的;服務(wù)訪問API(Service Access API),是客戶端用來獲取服務(wù)的實(shí)例的。服務(wù)訪問API一般允許但是不要求客戶端指定某種選擇提供者的條件。如果沒有這樣的規(guī)定,API就會(huì)返回默認(rèn)實(shí)現(xiàn)的一個(gè)實(shí)例。服務(wù)訪問API是"靈活

10、的靜態(tài)工廠",它構(gòu)成了服務(wù)提供者框架的基礎(chǔ)。服務(wù)提供者框架的第四個(gè)組件是可選的:服務(wù)提供者接口(Service Provider Interface),提供者實(shí)現(xiàn)它來創(chuàng)建其服務(wù)實(shí)現(xiàn)的實(shí)例。如果沒有服務(wù)提供者接口,實(shí)現(xiàn)就按照類名稱注冊,并通過反射方式進(jìn)行實(shí)例化(見第53條)。對(duì)于JDBC來說,Connection就是它的服務(wù)接口,DriverManager.registerDriver是提供者注冊API,DriverManager.getConnection是服務(wù)訪問API,Driver就是服務(wù)提供者接口。服務(wù)提供者框架模式有著無數(shù)種變體。例如,服務(wù)訪問API可以利用Adapter模式

11、Gamma95,p.139,返回比提供者需要的更豐富的服務(wù)接口。下面是一個(gè)簡單的實(shí)現(xiàn),包含一個(gè)服務(wù)提供者接口和一個(gè)默認(rèn)提供者:/ Service provider framework sketch/ Service interfacepublic interface Service . / Service-specific methods go here/ Service provider interfacepublic interface Provider Service newService();/ Noninstantiable class for service registratio

12、n and accesspublic class Services private Services() / Prevents instantiation (Item 4)/ Maps service names to servicesprivate static final Map providers =new ConcurrentHashMap();public static final String DEFAULT_PROVIDER_NAME = ""/ Provider registration APIpublic static void registerDefau

13、ltProvider(Provider p) registerProvider(DEFAULT_PROVIDER_NAME, p);public static void registerProvider(String name, Provider p)providers.put(name, p);/ Service access APIpublic static Service newInstance() return newInstance(DEFAULT_PROVIDER_NAME);public static Service newInstance(String name) Provid

14、er p = providers.get(name);if (p = null)throw new IllegalArgumentException("No provider registered with name: " + name);return p.newService();靜態(tài)工廠方法的第四大優(yōu)勢在于,在創(chuàng)建參數(shù)化類型實(shí)例的時(shí)候,它們使代碼變得更加簡潔。遺憾的是,在調(diào)用參數(shù)化類的構(gòu)造器時(shí),即使類型參數(shù)很明顯,也必須指明。這通常要求得接連兩次提供類型參數(shù):Map> m =new HashMap>();隨著類型參數(shù)變得越來越長,越來越復(fù)雜,這一冗長的說明也

15、很快變得痛苦起來。但是有了靜態(tài)工廠方法,編譯器就可以替你找到類型參數(shù)。這被稱作type inference。例如,假設(shè)HashMap提供了這個(gè)靜態(tài)工廠:public static HashMap newInstance() return new HashMap();你就可以用下面這句簡潔的代碼代替上面這段繁瑣的聲明:Map> m = HashMap.newInstance();總有一天,Java將能夠在構(gòu)建器調(diào)用以及方法調(diào)用中執(zhí)行這種type inference,但到發(fā)行版本1.6為止暫時(shí)還無法這么做。遺憾的是,到發(fā)行版本1.6為止,標(biāo)準(zhǔn)的集合實(shí)現(xiàn)如HashMap并沒有工廠方法,但是可以

16、把這些方法放在你自己的工具類中。更重要的是,可以把這樣的靜態(tài)工廠放在你自己的參數(shù)化的類中。靜態(tài)工廠方法的主要缺點(diǎn)在于,類如果不含公有的或者受保護(hù)的構(gòu)造器,就不能被子類化。對(duì)于公有的靜態(tài)工廠所返回的非公有類,也同樣如此。例如,要想將Collections Framework中的任何方便的實(shí)現(xiàn)類子類化,這是不可能的。但是這樣也許會(huì)因禍得福,因?yàn)樗膭?lì)程序員使用復(fù)合(composition),而不是繼承(見第16條)。靜態(tài)工廠方法的第二個(gè)缺點(diǎn)在于,它們與其他的靜態(tài)方法實(shí)際上沒有任何區(qū)別。在API文檔中,它們沒有像構(gòu)造器那樣在API文檔中明確標(biāo)識(shí)出來,因此,對(duì)于提供了靜態(tài)工廠方法而不是構(gòu)造器的類來說,要想查明如何實(shí)例化一個(gè)類,這是非常困難的。Javadoc工具總有一天會(huì)注意到靜態(tài)工廠方法。同時(shí),你通過在類或者接口注釋中關(guān)注靜態(tài)工廠,并遵守標(biāo)準(zhǔn)的命名習(xí)慣,也可以縮小這一劣勢。下面是靜態(tài)工廠方法的一些慣用名稱:valueOf-不太嚴(yán)格地講,該方法返回的實(shí)例與它的參數(shù)具有相同的值。這樣的靜態(tài)工廠方法實(shí)際上是類型轉(zhuǎn)換方法。of-valueOf的一種更為簡潔的替代,在EnumSet(見第32條)中使用并流行起來。getInstance-返回的實(shí)例是通過方法的參數(shù)來描述的,但是不能夠說與參數(shù)具有同樣的值。對(duì)于Singleton來說,該方法沒有參數(shù),并返回唯

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。

評(píng)論

0/150

提交評(píng)論