軟件設(shè)計(jì)原則與模式-基于Java-Python語(yǔ)言實(shí)現(xiàn)(微課版)課件全套 第1-4章 軟件設(shè)計(jì)原則、行為型模式_第1頁(yè)
軟件設(shè)計(jì)原則與模式-基于Java-Python語(yǔ)言實(shí)現(xiàn)(微課版)課件全套 第1-4章 軟件設(shè)計(jì)原則、行為型模式_第2頁(yè)
軟件設(shè)計(jì)原則與模式-基于Java-Python語(yǔ)言實(shí)現(xiàn)(微課版)課件全套 第1-4章 軟件設(shè)計(jì)原則、行為型模式_第3頁(yè)
軟件設(shè)計(jì)原則與模式-基于Java-Python語(yǔ)言實(shí)現(xiàn)(微課版)課件全套 第1-4章 軟件設(shè)計(jì)原則、行為型模式_第4頁(yè)
軟件設(shè)計(jì)原則與模式-基于Java-Python語(yǔ)言實(shí)現(xiàn)(微課版)課件全套 第1-4章 軟件設(shè)計(jì)原則、行為型模式_第5頁(yè)
已閱讀5頁(yè),還剩463頁(yè)未讀 繼續(xù)免費(fèi)閱讀

下載本文檔

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

文檔簡(jiǎn)介

《軟件設(shè)計(jì)原則與模式》本課程分為兩個(gè)部分第一章軟件設(shè)計(jì)原則設(shè)計(jì)模式(包括三部分):第二章創(chuàng)建模式第三章結(jié)構(gòu)模式第四章行為模式第一章軟件設(shè)計(jì)原則與UML簡(jiǎn)介寧波城市職業(yè)技術(shù)學(xué)院郭雙宙軟件設(shè)計(jì)什么是軟件(1)運(yùn)行時(shí),能夠提供所要求功能和性能的指令或計(jì)算機(jī)程序集合。(2)程序能夠滿意地處理信息的數(shù)據(jù)結(jié)構(gòu)。(3)描述程序功能需求以及程序如何操作和使用所要求的文檔。這時(shí)軟件設(shè)計(jì)就至關(guān)重要了。軟件設(shè)計(jì)包括軟件的結(jié)構(gòu)設(shè)計(jì),數(shù)據(jù)設(shè)計(jì),接口設(shè)計(jì)和過(guò)程設(shè)計(jì).結(jié)構(gòu)設(shè)計(jì)是指:定義軟件系統(tǒng)各主要部件之間的關(guān)系數(shù)據(jù)設(shè)計(jì)是指:將模型轉(zhuǎn)換成數(shù)據(jù)結(jié)構(gòu)的定義接口設(shè)計(jì)是指:軟件內(nèi)部,軟件和操作系統(tǒng)間以及軟件和人之間如何通信過(guò)程設(shè)計(jì)是指:系統(tǒng)結(jié)構(gòu)部件轉(zhuǎn)換成軟件的過(guò)程描述軟件質(zhì)量如何設(shè)計(jì)才能保證質(zhì)量?下面給出軟件設(shè)計(jì)的5個(gè)一般原則:(1)要有分層的組織結(jié)構(gòu),便于對(duì)軟件各個(gè)構(gòu)件

進(jìn)行控制;(2)應(yīng)形成具有獨(dú)立功能特征的模塊(模塊化);(3)應(yīng)有性質(zhì)不同、可區(qū)分的數(shù)據(jù)和過(guò)程描述(表達(dá)式);(4)應(yīng)使模塊之間、模塊與外部環(huán)境之間的接口復(fù)雜性盡量減??;(5)應(yīng)利用軟件需求分析中得到的信息和可重復(fù)的方法。合格軟件的10個(gè)標(biāo)準(zhǔn)怎樣評(píng)價(jià)設(shè)計(jì)的軟件是合格的?通常有如下標(biāo)準(zhǔn):(1)

可靠性

軟件在運(yùn)行過(guò)程中避免可能發(fā)生故障的能力且一旦發(fā)生故障后,具有解脫和排除故障的能力。(2)

健壯性(魯棒性)

指軟件對(duì)于規(guī)范要求以外的輸入能夠判斷出這個(gè)輸入不符合規(guī)范要求,并能有合理的處理方式。

軟件健壯性是非常重要的軟件外部量度標(biāo)準(zhǔn)。(3)

可修改性

要求軟件有良好的結(jié)構(gòu)和完備的文檔,系統(tǒng)易于調(diào)整。(4)

容易理解

包括:文檔清晰可讀和軟件本身具有簡(jiǎn)單明了的結(jié)構(gòu)。(5)程序簡(jiǎn)便包括:代碼簡(jiǎn)單易懂可讀性強(qiáng),系統(tǒng)運(yùn)行易上手。合格軟件的10個(gè)標(biāo)準(zhǔn)(6)

可測(cè)試性

能設(shè)計(jì)一個(gè)適當(dāng)?shù)臄?shù)據(jù)集合用于測(cè)試所建立的系統(tǒng),并保證系統(tǒng)能得到全面的檢驗(yàn)。(7)

效率性

一般用程序的執(zhí)行時(shí)間和所占用的內(nèi)存容量來(lái)度量。

在達(dá)到功能指標(biāo)要求的前提下,程序運(yùn)行所需時(shí)間愈短和占用存儲(chǔ)容量愈小,則效率愈高。(8)標(biāo)準(zhǔn)化原則

在結(jié)構(gòu)上實(shí)現(xiàn)開(kāi)放,基于業(yè)界開(kāi)放式標(biāo)準(zhǔn),符合國(guó)家和信息產(chǎn)業(yè)部的規(guī)范。(9)先進(jìn)性

滿足客戶需求,系統(tǒng)性能可靠,易于維護(hù)。(10)可擴(kuò)展性

軟件設(shè)計(jì)完要留有升級(jí)接口和升級(jí)空間。什么是軟件設(shè)計(jì)原則要想得到一個(gè)滿意的設(shè)計(jì)結(jié)果,需要有基本設(shè)計(jì)原則的指導(dǎo),系統(tǒng)化的設(shè)計(jì)方法科學(xué)嚴(yán)格的評(píng)審機(jī)制相結(jié)合。才能達(dá)到預(yù)想的目的軟件設(shè)計(jì)原則從宏觀上指導(dǎo)著軟件設(shè)計(jì),但軟件設(shè)計(jì)的具體實(shí)現(xiàn)還要遵循軟件設(shè)計(jì)的基本準(zhǔn)則。軟件設(shè)計(jì)原則滿足了軟件開(kāi)發(fā)過(guò)程中常用的軟件設(shè)計(jì)原則,就容易實(shí)現(xiàn)上面的諸多要求。下面介紹七種常用的軟件設(shè)計(jì)原則?!伴_(kāi)-閉”原則里氏代換原則依賴倒置原則接口隔離原則合成/聚合復(fù)用原則迪米特法則單一職責(zé)原則第一節(jié)“開(kāi)-閉”原則(Open-ClosedPrinciple,OCP)開(kāi)-閉原則:一個(gè)軟件實(shí)體應(yīng)當(dāng)對(duì)擴(kuò)展開(kāi)放,對(duì)修改關(guān)閉。滿足“開(kāi)一閉”原則的設(shè)計(jì)可以給一個(gè)軟件系統(tǒng)兩個(gè)優(yōu)點(diǎn):可擴(kuò)展性:通過(guò)擴(kuò)展已有的軟件系統(tǒng),可以提供新的行為,以滿足對(duì)軟件的新需求,使變化中的軟件系統(tǒng)有一定的適應(yīng)性和靈活性。穩(wěn)定性和延續(xù)性:不修改已有的軟件模塊特別是最重要的抽象層模塊,這就使變化中的軟件系統(tǒng)有一定的穩(wěn)定性和延續(xù)性。具有這兩個(gè)優(yōu)點(diǎn)的軟件系統(tǒng)是一個(gè)在高層次上實(shí)現(xiàn)了復(fù)用的系統(tǒng),也是一個(gè)易于維護(hù)的系統(tǒng)?!伴_(kāi)-閉”原則常見(jiàn)實(shí)際應(yīng)用:現(xiàn)在的程序常有升級(jí),且升級(jí)后原有功能不變,還可能會(huì)增加新的功能。例子:玉帝招安美猴王當(dāng)年大鬧天宮的美猴王便是對(duì)玉帝天庭的新挑戰(zhàn)。美猴王說(shuō):“皇帝輪流做,明年到我家。只教他搬出去,將天宮讓與我?!睂?duì)于這項(xiàng)挑戰(zhàn),太白金星給玉皇大帝提出的建議是:“……降一道招安圣旨,把他宣來(lái)上界……?!睋Q言之,不勞師動(dòng)眾、不破壞天規(guī)便是“閉”,收仙有道便是“開(kāi)”。招安之法便是玉帝天庭的“開(kāi)-閉”原則,通過(guò)增加一個(gè)“弼馬溫”宮職封給美猴王,便可使現(xiàn)有系統(tǒng)滿足變化了的需求而不必更改天庭的既有秩序,如下圖所示。招安之法的關(guān)鍵便是不允許更改現(xiàn)有的天庭秩序,但允許將妖猴納入現(xiàn)有秩序中,從而擴(kuò)展了這一秩序。用面向?qū)ο蟮恼Z(yǔ)言來(lái)講,不允許更改的是系統(tǒng)的抽象層,而允許擴(kuò)展的是系統(tǒng)的實(shí)現(xiàn)層。文武仙師是抽象層,太白金星等就是實(shí)現(xiàn)層。第二節(jié)里氏代換原則(LiskovSubstitutionPrinciple,LSP)里氏代換原則的意思是:程序中如果使用一個(gè)父類,那么一定可以用其子類替換,而且被替換處根本不能察覺(jué)出父類對(duì)象和子類對(duì)象的區(qū)別。注意:只有子類可以替換父類,軟件單位的功能才能不受影響,父類才能真正被復(fù)用,而子類也能夠在父類的基礎(chǔ)上增加新功能。反過(guò)來(lái)的代換不成立,即父類不能代替子類。例子:乘馬說(shuō)《墨子?小取》中說(shuō):“白馬,馬也;乘白馬,乘馬也。驪馬(黑馬),馬也;乘驪馬,乘馬也?!边@個(gè)說(shuō)法的結(jié)構(gòu)圖如圖所示。對(duì)人來(lái)說(shuō),騎小黑馬和騎白龍馬結(jié)果是一樣的,都是騎馬。這就是里氏代換原則的一個(gè)實(shí)際應(yīng)用。第三節(jié)依賴倒置原則(DependenceInversionPrinciple)依賴倒置原則要求客戶端依賴于抽象耦合。依賴倒置原則表述:要針對(duì)接口編程,不要針對(duì)實(shí)現(xiàn)編程。意思是:應(yīng)當(dāng)使用接口和抽象類進(jìn)行變量的類型聲明,參量的類型聲明,方法的返還類型聲明等。不遵守依賴倒置原則的例子:privateArrayList

list=newArrayList();publicArrayList

method(ArrayList

sample){

……}遵守依賴倒置原則的例子:privateListlist=newArrayList();publicListmethod(Listsample){ ……}第四節(jié)接口隔離原則(InterfaceSegregationPrinciple,ISP)接口隔離原則的意思是使用多個(gè)專門(mén)的接口比使用單一的總接口要好。理由:一個(gè)類對(duì)另外一個(gè)類的依賴是建立在最小的接口上。胖接口會(huì)導(dǎo)致他們的客戶程序之間產(chǎn)生不正常的并且有害的耦合關(guān)系。當(dāng)一個(gè)客戶程序要求該胖接口進(jìn)行一個(gè)改動(dòng)時(shí),會(huì)影響到所有其他的客戶程序。因此客戶程序應(yīng)該僅僅依賴他們實(shí)際需要調(diào)用的方法。使用場(chǎng)合:提供調(diào)用者需要的方法,屏蔽不需要的方法。例子:電子商務(wù)系統(tǒng)的訂單類電子商務(wù)系統(tǒng)的訂單類有三個(gè)地方會(huì)使用到:第一:匿名用戶,只能有查詢方法。第二:注冊(cè)用戶,有添加和查詢訂單方法。第三:管理后臺(tái),添加刪除修改查詢。根據(jù)接口隔離原則(ISP),一個(gè)類對(duì)另外一個(gè)類的依賴性應(yīng)當(dāng)是建立在最小的接口上,也就是說(shuō),對(duì)于匿名用戶,它只能依賴有一個(gè)查詢方法的接口。訂單類結(jié)構(gòu)圖該系統(tǒng)的UML結(jié)構(gòu)圖如圖所示。系統(tǒng)實(shí)現(xiàn)見(jiàn)書(shū)。第五節(jié)合成/聚合復(fù)用原則(CARP)合成和聚合都是對(duì)象建模中的關(guān)聯(lián)(Association)關(guān)系。聚合表示整體與部分的關(guān)系,表示“含有”,整體由部分組合而成,部分可以脫離整體作為一個(gè)獨(dú)立的個(gè)體存在。合成則是一種更強(qiáng)的聚合,部分組成整體,而且不可分割,部分不能脫離整體而單獨(dú)存在。在合成關(guān)系中,部分和整體的生命周期一樣,合成的新的對(duì)象完全支配其組成部分,包括他們的創(chuàng)建和銷毀。一個(gè)合成關(guān)系中成分對(duì)象是不能與另外一個(gè)合成關(guān)系共享。本原則要求:要盡量使用合成/聚合,不要使用繼承。合成/聚合和繼承是實(shí)現(xiàn)復(fù)用的兩個(gè)基本途徑。合成復(fù)用原則是指盡量使用合成/聚合,而不是使用繼承。關(guān)于繼承關(guān)系只有當(dāng)以下三個(gè)條件全部被滿足時(shí),才應(yīng)使用繼承關(guān)系:子類是超類的一個(gè)特殊種類,而不是超類的一個(gè)角色,也就是區(qū)分“Has-A”和“Is-A”?!癐s-A”關(guān)系符合繼承關(guān)系,“Has-A”關(guān)系應(yīng)當(dāng)使用聚合來(lái)描述。永遠(yuǎn)不會(huì)出現(xiàn)需要將子類換成另外一個(gè)類的子類的情況。如果不能肯定將來(lái)是否會(huì)變成另外一個(gè)子類的話,就不要使用繼承。子類具有擴(kuò)展超類的責(zé)任,而不是具有置換掉或注銷掉超類的責(zé)任。

如果一個(gè)子類需要大量的置換掉超類的行為,那么這個(gè)類就不應(yīng)該是這個(gè)超類的子類?!癏as-A”與“Is-A”的區(qū)別錯(cuò)誤的使用繼承而不是合成/聚合的一個(gè)常見(jiàn)原因是錯(cuò)誤地把“Has-A”當(dāng)成了“Is-A”?!癐s-A”代表一個(gè)類是另外一個(gè)類的一種;“Has-A”代表一個(gè)類是另外一個(gè)類的一個(gè)角色,而不是另外一個(gè)類的特殊種類。例一:不使用合成/聚合復(fù)用原則導(dǎo)致錯(cuò)誤的使用繼承而不是合成/聚合的一個(gè)常見(jiàn)的原因是錯(cuò)誤的把“Has-A”當(dāng)作“Is-A”。例如:在上圖中,一個(gè)人只能有一個(gè)角色,兼職就不行了。一個(gè)人無(wú)法同時(shí)擁有多個(gè)角色,是“雇員”就不能再是“學(xué)生”了,這顯然是不合理的。例二:使用合成/聚合復(fù)用原則實(shí)際上,雇員、經(jīng)理、學(xué)生描述的只是一種角色,比如一個(gè)人是“經(jīng)理”必然是“雇員”,另外一個(gè)人可能是“學(xué)生雇員”。例一的錯(cuò)誤源于把“角色”的等級(jí)結(jié)構(gòu)與“人”的等級(jí)結(jié)構(gòu)混淆起來(lái),誤把“Has-A”當(dāng)作“Is-A”。修正后的結(jié)構(gòu)如圖所示?,F(xiàn)在編程就靈活多了,把角色作為人的屬性,同一個(gè)人可以很方便的具有不同角色。第六節(jié)迪米特法則(LawofDemeter,LoD)軟件結(jié)構(gòu)設(shè)計(jì)的總原則是“低耦合,高內(nèi)聚”。低耦合是指讓每個(gè)模塊盡可能的獨(dú)立完成某個(gè)特定的子功能。高內(nèi)聚是指一個(gè)軟件模塊是由相關(guān)性很強(qiáng)的代碼組成,只負(fù)責(zé)一項(xiàng)任務(wù),也就是單一職責(zé)原則。迪米特法則解析無(wú)論是面向過(guò)程編程還是面向?qū)ο缶幊?,只有使各個(gè)模塊之間的耦合盡量低,才能提高代碼的復(fù)用率。怎么樣編程才能做到耦合盡量低呢?這正是迪米特法則要去完成的。迪米特法則又叫做最少知識(shí)原則,就是說(shuō),一個(gè)對(duì)象應(yīng)當(dāng)對(duì)其他對(duì)象有盡可能少的了解。迪米特法則核心觀念就是類間解耦,弱耦合,只有弱耦合了以后,類的復(fù)用率才能提高。例一:不使用迪米特法則我們?cè)诎惭b軟件時(shí),經(jīng)常有一個(gè)導(dǎo)向動(dòng)作:第一步是確認(rèn)是否安裝,第二步是確認(rèn)License,最后再選擇安裝目錄;這是一個(gè)典型的順序執(zhí)行動(dòng)作,具體到程序中就是:調(diào)用一個(gè)或者多個(gè)類,先執(zhí)行第一個(gè)方法,然后是第二個(gè)方法,根據(jù)返回結(jié)果再來(lái)看是否調(diào)用第三個(gè)方法,其類圖如圖所示:實(shí)現(xiàn)代碼見(jiàn)書(shū)P9。方法類型是public缺點(diǎn):可直接調(diào)用任一方法例子:使用迪米特法則后重構(gòu)后的類圖如圖所示。

實(shí)現(xiàn)代碼見(jiàn)書(shū)P11。方法類型是private注意:迪米特法則要求類盡量不要對(duì)外公布太多的public方法和非靜態(tài)的public變量,多使用private、protected等訪問(wèn)權(quán)限。第七節(jié)單一職責(zé)原則(SingleresponsibilitypincipleSRP)單一職責(zé)原則在編程過(guò)程中經(jīng)常能遇上,比方說(shuō)需要修改用戶名和密碼,可能的寫(xiě)法如下所示。voidchange(StringuserName,Stringpassword);如果只需要修改一個(gè)數(shù)據(jù),上面代碼則要求必須將兩個(gè)數(shù)據(jù)都傳進(jìn)去,這就違反了單一職責(zé)原則。原因很簡(jiǎn)單:方法職責(zé)不單一。正確的做法應(yīng)該是:voidchangeName(StringuserName);voidchangePassword(Stringpassword);不用單一職責(zé)原則例子:用戶信息管理系統(tǒng)一般來(lái)說(shuō)項(xiàng)目都要涉及到用戶、機(jī)構(gòu)、角色管理這些模塊,基本上使用的都是RBAC模型(基于角色的訪問(wèn)控制Role-BasedAccessControl,RBAC),這確實(shí)是很好的一個(gè)解決辦法。該系統(tǒng)應(yīng)有如下功能:用戶管理、修改用戶的信息、增加機(jī)構(gòu)(一個(gè)人屬于多個(gè)機(jī)構(gòu))、增加角色等。用戶有這么的信息和行為要維護(hù),我們把這些寫(xiě)到一個(gè)接口中,因?yàn)槎际怯脩艄芾眍?。這個(gè)用戶信息管理系統(tǒng)結(jié)構(gòu)圖如右圖所示??梢钥闯鲞@個(gè)接口設(shè)計(jì)中用戶的屬性用戶的行為沒(méi)有分開(kāi),這是一個(gè)嚴(yán)重的錯(cuò)誤!正確的設(shè)計(jì)思路應(yīng)該把用戶的信息抽取成一個(gè)BO(BussinessObject,業(yè)務(wù)對(duì)象);把行為抽取成一個(gè)BL(BusinessLogic,業(yè)務(wù)邏輯),按照這個(gè)思路對(duì)類圖進(jìn)行修正,重新拆封成兩個(gè)接口,IUserBO負(fù)責(zé)用戶的屬性,其職責(zé)就是收集和反饋用戶的屬性信息;IUserBL負(fù)責(zé)用戶的行為,完成用戶信息的維護(hù)和變更。職責(zé)劃分后的類圖如圖所示。代碼見(jiàn)書(shū)P15

UML簡(jiǎn)介8.1. 依賴(Dependency)實(shí)體之間的“依賴”關(guān)系表示一個(gè)實(shí)體的規(guī)范發(fā)生變化后,可能影響依賴于它的其他實(shí)例。更具體地說(shuō),它可轉(zhuǎn)換為對(duì)不在實(shí)例作用域內(nèi)的一個(gè)類或?qū)ο蟮娜魏晤愋偷囊?。它包括一個(gè)局部變量,對(duì)通過(guò)方法調(diào)用而獲得對(duì)一個(gè)對(duì)象的引用(如圖所示),或者對(duì)一個(gè)類的靜態(tài)方法的引用(同時(shí)不存在那個(gè)類的一個(gè)實(shí)例)。

“依賴”也可以用來(lái)表示包和包之間的關(guān)系。由于包中含有類,所以你可根據(jù)那些包中的各個(gè)類之間的關(guān)系,表示出包和包的關(guān)系。8.2. 關(guān)聯(lián)(Association)實(shí)體之間的一個(gè)結(jié)構(gòu)化關(guān)系表明對(duì)象是相互連接的。箭頭是可選的,它用于指定導(dǎo)航能力。如果沒(méi)有箭頭,暗示是一種雙向的導(dǎo)航能力。在Java中,關(guān)聯(lián)轉(zhuǎn)換為一個(gè)實(shí)例作用域的變量,就像下圖所展示的代碼那樣。可為一個(gè)關(guān)聯(lián)附加其他修飾符。多重性(Multiplicity)修飾符暗示著實(shí)例之間的關(guān)系。在示范代碼中,Employee可以有0個(gè)或更多的TimeCard對(duì)象。但是,每個(gè)TimeCard只從屬于單獨(dú)一個(gè)Employee。8.3. 聚合(Aggregation)聚合是關(guān)聯(lián)的一種形式,代表兩個(gè)類之間的整體/局部關(guān)系。聚合暗示著整體在概念上處于比局部更高的一個(gè)級(jí)別,而關(guān)聯(lián)暗示兩個(gè)類在概念上位于相同的級(jí)別。聚合也轉(zhuǎn)換成Java中的一個(gè)實(shí)例作用域變量。關(guān)聯(lián)和聚合的區(qū)別純粹是概念上的,而且嚴(yán)格反映在語(yǔ)義上。聚合還暗示著實(shí)例圖中不存在回路。換言之,只能是一種單向關(guān)系。8.4. 組合(Composition)組合是聚合的一種特殊形式,暗示“局部”在“整體”內(nèi)部的生存期職責(zé)。合成也是非共享的。所以,雖然局部不一定要隨整體的銷毀而被銷毀,但整體要么負(fù)責(zé)保持局部的存活狀態(tài),要么負(fù)責(zé)將其銷毀。局部不可與其他整體共享。但是,整體可將所有權(quán)轉(zhuǎn)交給另一個(gè)對(duì)象,后者隨即將承擔(dān)生存期職責(zé)。Employee和TimeCard的關(guān)系或許更適合表示成“合成”,而不是表示成“關(guān)聯(lián)”。8.5. 泛化(Generalization)泛化表示一個(gè)更泛化的元素和一個(gè)更具體的元素之間的關(guān)系。泛化是用于對(duì)繼承進(jìn)行建模的UML元素。在Java中,用extends關(guān)鍵字來(lái)直接表示這種關(guān)系。8.6. 實(shí)現(xiàn)(Realization)實(shí)例(圖1-8-6)關(guān)系指定兩個(gè)實(shí)體之間的一個(gè)合同(接口)。換言之,一個(gè)實(shí)體定義一個(gè)合同,而另一個(gè)實(shí)體保證履行該合同。對(duì)Java應(yīng)用程序進(jìn)行建模時(shí),實(shí)現(xiàn)關(guān)系可直接用implements關(guān)鍵字來(lái)表示。第二章創(chuàng)建模式(CreativnalPattem)創(chuàng)建模式(CreativnalPattem)創(chuàng)建型模式抽象了實(shí)例化過(guò)程。該模式幫助一個(gè)系統(tǒng)如何獨(dú)立創(chuàng)建、組合和表示他的那些對(duì)象。一些系統(tǒng)在創(chuàng)建對(duì)象時(shí),需要?jiǎng)討B(tài)地決定怎樣創(chuàng)建對(duì)象,創(chuàng)建哪些對(duì)象,如何組合和表示這些對(duì)象。創(chuàng)建模式描述了怎樣構(gòu)造和封裝這些動(dòng)態(tài)的決定。創(chuàng)建模式分為類的創(chuàng)建模式和對(duì)象的創(chuàng)建模式兩種。類創(chuàng)建型模式使用繼承改變被實(shí)例化的類。對(duì)象創(chuàng)建型模式將實(shí)例化委托給另一個(gè)對(duì)象。創(chuàng)建模式包括:工廠模式第一節(jié)簡(jiǎn)單工廠模式第二節(jié)工廠方法模式第三節(jié)抽象工廠模式第四節(jié)單例模式第五節(jié)多例模式第六節(jié)建造模式第七節(jié)原型模式工廠模式工廠模式專門(mén)負(fù)責(zé)將大量有共同接口的類實(shí)例化。工廠模式可以動(dòng)態(tài)決定將哪一個(gè)類實(shí)例化,不必事先知道每次要實(shí)例化哪一個(gè)類。工廠模式有以下三種形態(tài):簡(jiǎn)單工廠模式:又稱靜態(tài)工廠方法模式。工廠方法模式:又稱多態(tài)性工廠模式。抽象工廠模式:又稱工具箱模式。第一節(jié) 簡(jiǎn)單工廠(SimpleFactory)模式簡(jiǎn)單工廠模式是類的創(chuàng)建模式,又叫做靜態(tài)工廠方法(StaticFactoryMethod)模式。簡(jiǎn)單工廠模式是由一個(gè)工廠對(duì)象決定創(chuàng)建出哪一種產(chǎn)品類的實(shí)例。簡(jiǎn)單工廠模式將客戶類和工廠類分開(kāi)??蛻纛悓?duì)象任何時(shí)候需要某種產(chǎn)品,只需向工廠請(qǐng)求即可??蛻纛悓?duì)象無(wú)須修改就可以接納新產(chǎn)品。缺點(diǎn)是當(dāng)產(chǎn)品修改時(shí),工廠類也要做相應(yīng)的修改。如:如何創(chuàng)建及如何向客戶端提供。實(shí)用案例:小型農(nóng)場(chǎng)系統(tǒng)例子:女?huà)z摶土造人話說(shuō):“天地開(kāi)辟,未有人民,女?huà)z摶土為人?!迸?huà)z用土和八卦爐造出了大活人,但在女?huà)z造出人之前,人的概念只存在于女?huà)z的大腦里。女?huà)z造人,這就是簡(jiǎn)單工廠模式的應(yīng)用。程序結(jié)構(gòu)圖如圖所示。1.2 簡(jiǎn)單工廠模式的結(jié)構(gòu)簡(jiǎn)單工廠模式是根據(jù)傳入的參數(shù)來(lái)決定到底應(yīng)該創(chuàng)建那個(gè)類的事例出來(lái)。下圖是簡(jiǎn)單工廠模式的一般結(jié)構(gòu)。由圖可以看出,簡(jiǎn)單工廠模式由工廠角色、抽象產(chǎn)品角色、產(chǎn)品角色這三部分組成。工廠角色(Creator):這是工廠方法模式的核心,但客戶端調(diào)用他的工廠方法的時(shí)候,返回給客戶端的是產(chǎn)品角色的一個(gè)類的對(duì)象實(shí)例。產(chǎn)品角色(ConcreteProduct):簡(jiǎn)單工廠方法所創(chuàng)建的任何一個(gè)對(duì)象都是這個(gè)角色的一個(gè)類的對(duì)象實(shí)例。抽象產(chǎn)品角色(Product):定義了產(chǎn)品角色所共有的共性,通常由一個(gè)java接口或者是一個(gè)java抽象類。如果具體產(chǎn)品之間沒(méi)有共同的商業(yè)邏輯,就用java接口,如果有共同的商業(yè)邏輯,就用一個(gè)java抽象類。Python實(shí)現(xiàn)案例簡(jiǎn)單工廠模式的缺點(diǎn)是當(dāng)在產(chǎn)品角色中再增加一個(gè)類的時(shí)候,工廠方法必須有發(fā)生相應(yīng)的改變,這就導(dǎo)致了擴(kuò)展性不符合開(kāi)-閉原則?!芭?huà)z造人”簡(jiǎn)單工廠模式的實(shí)現(xiàn):P23女?huà)z采用簡(jiǎn)單工廠模式成功造出了人,不過(guò)還沒(méi)有膚色和性別。

簡(jiǎn)單工廠模式使用實(shí)例簡(jiǎn)單工廠模式是在什么場(chǎng)景下使用呢,下面以登錄功能為例說(shuō)明:假如應(yīng)用系統(tǒng)需要支持多種登錄方式如:口令認(rèn)證、域認(rèn)證(口令認(rèn)證通常是去數(shù)據(jù)庫(kù)中驗(yàn)證用戶,而域認(rèn)證則是需要到微軟的域中驗(yàn)證用戶)。自然的做法就是建立一個(gè)各種登錄方式都適用的接口,如圖所示。代碼見(jiàn)書(shū)P25簡(jiǎn)單工廠模式的優(yōu)缺點(diǎn)1.3.1簡(jiǎn)單工廠模式的優(yōu)點(diǎn)模式的核心是工廠類。這個(gè)類含有必要的邏輯判斷,可以決定在什么時(shí)候創(chuàng)建哪一個(gè)登錄驗(yàn)證類的實(shí)例,而調(diào)用者則可以免除直接創(chuàng)建對(duì)象的責(zé)任。簡(jiǎn)單工廠模式通過(guò)這種做法實(shí)現(xiàn)了對(duì)責(zé)任的分割,當(dāng)系統(tǒng)引入新的登錄方式的時(shí)候無(wú)需修改調(diào)用者。1.3.2簡(jiǎn)單工廠模式的缺點(diǎn)這個(gè)工廠類集中了所有的創(chuàng)建邏輯,當(dāng)有復(fù)雜的多層次等級(jí)結(jié)構(gòu)時(shí),所有的業(yè)務(wù)邏輯都在這個(gè)工廠類中實(shí)現(xiàn)。什么時(shí)候它不能工作了,整個(gè)系統(tǒng)都會(huì)受到影響。練習(xí):小型農(nóng)場(chǎng)系統(tǒng)實(shí)現(xiàn)有一個(gè)農(nóng)場(chǎng)公司,專門(mén)向市場(chǎng)銷售各類水果,在這個(gè)系統(tǒng)里需要描述三種水果:葡萄(Grappe)、草莓(Stuawberry)、蘋(píng)果(Apple)。水果與其他植物不同,最終可以采摘食用,那么一個(gè)自然的做法是建立一個(gè)各種水果都適用的接口,以便與其他農(nóng)場(chǎng)里的植物區(qū)分開(kāi)來(lái),UML類圖設(shè)計(jì)如圖所示。代碼見(jiàn)書(shū)P27第二節(jié) 工廠方法(FactoryMethod)模式工廠方法模式:核心工廠類不再負(fù)責(zé)所有產(chǎn)品的創(chuàng)建,而是將具體創(chuàng)建的工作交給子類去做,成為一個(gè)抽象工廠角色,僅負(fù)責(zé)給出具體工廠類必須實(shí)現(xiàn)的接口,而不接觸哪一個(gè)產(chǎn)品類應(yīng)當(dāng)被實(shí)例化這種細(xì)節(jié)。與簡(jiǎn)單工廠模式的區(qū)別:在工廠方法模式中,核心的工廠類不再負(fù)責(zé)所有具體產(chǎn)品實(shí)例的創(chuàng)建,而僅僅是需要負(fù)責(zé)給出具體工廠子類必須實(shí)現(xiàn)的接口,讓工廠子類去負(fù)責(zé)具體產(chǎn)品實(shí)例的創(chuàng)建。例子:女?huà)z造有膚色人種話說(shuō)這天女?huà)z娘娘采集黃土捏成人的形狀,然后放到八卦爐中燒制,最后放置到大地上生長(zhǎng),工藝過(guò)程是沒(méi)有錯(cuò)的,但是意外隨時(shí)都會(huì)發(fā)生:第一次烤泥人,嗞嗞嗞……,感覺(jué)應(yīng)該熟了,往大地上一放,哇,沒(méi)烤熟!于是一個(gè)白人誕生了?。ㄟ@也是缺乏經(jīng)驗(yàn)的最好證明)第二次烤泥人,這時(shí)盤(pán)古經(jīng)過(guò),看見(jiàn)女?huà)z娘娘在此,便停下聊了會(huì)兒天。這天上一日,地上十年,聊著聊著不覺(jué)過(guò)了晌午,女?huà)z這才想起八卦爐里還燒著人呢,趕緊取出來(lái)放到世間一看,嘿,熟過(guò)頭了,于是黑人誕生了!第三次烤泥人,這次不敢大意了,一邊燒制一邊察看,直到表皮微黃,外焦里嫩。嘿,真正好,于是女?huà)z心中的偶像:黃色人種出現(xiàn)了!不過(guò)如果時(shí)間還是稍長(zhǎng)了一些的話,那可就是黃里透黑喲……?,F(xiàn)在對(duì)造人過(guò)程進(jìn)行分析,該過(guò)程涉及三個(gè)對(duì)象:女?huà)z、八卦爐、三種不同膚色的人,女?huà)z可以使用場(chǎng)景類Client來(lái)表示,八卦爐類似于一個(gè)工廠,負(fù)責(zé)制造生產(chǎn)產(chǎn)品(即人類):三種不同膚色的人,他們都是同一個(gè)接口下的不同實(shí)現(xiàn)類,都是人嘛,只是膚色、語(yǔ)言不同,對(duì)于八卦爐來(lái)說(shuō)都是它生產(chǎn)出的產(chǎn)品。這個(gè)造人程序的UML結(jié)構(gòu)圖如圖所示。類圖比較簡(jiǎn)單,AbstractHumanFactory是一個(gè)抽象類,定義了一個(gè)八卦爐都具有的整體功能,八卦爐為實(shí)現(xiàn)類,完成具體的任務(wù):創(chuàng)建人類;Human接口是人類的總稱,其三個(gè)實(shí)現(xiàn)類分別為三類人種;女?huà)z娘娘類是一個(gè)場(chǎng)景類,負(fù)責(zé)模擬這個(gè)場(chǎng)景,執(zhí)行相關(guān)的任務(wù)。代碼見(jiàn)書(shū)P352.1 工廠方法模式的結(jié)構(gòu)工廠方法模式的結(jié)構(gòu)如圖所示。從圖可以看出,這個(gè)使用了工廠方法模式的系統(tǒng)涉及到以下的角色:工廠方法模式結(jié)構(gòu)的示意性源代碼見(jiàn)書(shū)P33抽象工廠(Creator)角色:擔(dān)任這個(gè)角色的是工廠方法模式的核心,它是與應(yīng)用程序無(wú)關(guān)的。任何在模式中創(chuàng)建對(duì)象的工廠類必須實(shí)現(xiàn)這個(gè)接口。這里,這個(gè)角色由接口Creator扮演;在實(shí)際的系統(tǒng)中,這個(gè)角色也經(jīng)常用抽象類來(lái)實(shí)現(xiàn)。具體工廠(ConcreteCreator)角色:擔(dān)任這個(gè)角色的是實(shí)現(xiàn)了抽象工廠接口的具體Java類。具體工廠角色含有與應(yīng)用密切相關(guān)的邏輯,并且受到應(yīng)用程序的調(diào)用以創(chuàng)建產(chǎn)品對(duì)象。這里給出了兩個(gè)這樣的角色,也就是具體Java類Concretecreatorl和ConcreteCreatcr2。抽象產(chǎn)品(Product)角色:工廠方法模式所創(chuàng)建對(duì)象即產(chǎn)品對(duì)象的共同父類或共同擁有的接口。這里這個(gè)角色為接口Product。具體產(chǎn)品(ConcreteProduct)角色:這個(gè)角色實(shí)現(xiàn)了抽象產(chǎn)品角色所聲明的接口。工廠方法模式所創(chuàng)建的每一個(gè)對(duì)象都是某個(gè)具體產(chǎn)品角色的實(shí)例。這里這個(gè)角色由具體類ConcreteProductl和ConcreteProduct2扮演,它們都實(shí)現(xiàn)了Product接口。工廠方法模式的實(shí)際應(yīng)用導(dǎo)出功能有這么一個(gè)需求:XX系統(tǒng)需要支持對(duì)數(shù)據(jù)庫(kù)中的員工薪資進(jìn)行導(dǎo)出,并且支持多種格式如:HTML、CSV、PDF等,每種格式導(dǎo)出的結(jié)構(gòu)有所不同,比如:財(cái)務(wù)跟其他人對(duì)導(dǎo)出薪資的HTML格式要求可能會(huì)不一樣,因?yàn)樨?cái)務(wù)可能需要特定的格式方便核算或其他用途。如果使用簡(jiǎn)單工廠模式,則工廠類必定過(guò)于臃腫。因?yàn)楹?jiǎn)單工廠模式只有一個(gè)工廠類,它需要處理所有的創(chuàng)建的邏輯。假如以上需求暫時(shí)只支持3種導(dǎo)出的格式以及2種導(dǎo)出的結(jié)構(gòu),那工廠類則需要6個(gè)ifelse來(lái)創(chuàng)建6種不同的類型。如果日后需求不斷增加,則后果不堪設(shè)想。這時(shí)就需要工廠方法模式來(lái)處理以上需求,下面作詳細(xì)說(shuō)明。前面的例子用工廠方法模式設(shè)計(jì)時(shí),核心的工廠類不再負(fù)責(zé)所有的對(duì)象的創(chuàng)建,而是將具體創(chuàng)建的工作交給子類去做。這個(gè)核心類則搖身一變,成為了一個(gè)抽象工廠角色,僅負(fù)責(zé)給出具體工廠子類必須實(shí)現(xiàn)的接口,而不接觸哪一個(gè)類應(yīng)當(dāng)被實(shí)例化這種細(xì)節(jié)。這種進(jìn)一步抽象化的結(jié)果,使這種工廠方法模式可以用來(lái)允許系統(tǒng)在不修改具體工廠角色的情況下引進(jìn)新的產(chǎn)品,這一特點(diǎn)無(wú)疑使得工廠方法模式具有超過(guò)簡(jiǎn)單工廠模式的優(yōu)越性。針對(duì)以上需求設(shè)計(jì)的UML圖如圖所示。源代碼見(jiàn)書(shū)P38抽象工廠(ExportFactory)角色:是工廠方法模式核心,任何在模式中創(chuàng)建對(duì)象的工廠類必須實(shí)現(xiàn)這個(gè)接口。這個(gè)角色也常用抽象類實(shí)現(xiàn)。具體工廠(ExportHtmlFactory、ExportPdfFactory)角色:實(shí)現(xiàn)了抽象工廠接口的具體JAVA類。含有與業(yè)務(wù)密切相關(guān)的邏輯,并受使用者調(diào)用以創(chuàng)建導(dǎo)出類(如:ExportStandardHtmlFile)。抽象導(dǎo)出(ExportFile)角色:工廠方法模式所創(chuàng)建對(duì)象的超類,是所有導(dǎo)出類的共同父類或共同擁有的接口。這個(gè)角色也常用抽象類實(shí)現(xiàn)。具體導(dǎo)出(ExportStandardHtmlFile等)角色:實(shí)現(xiàn)了抽象導(dǎo)出(ExportFile)角色所聲明的接口,工廠方法模式所創(chuàng)建的每一個(gè)對(duì)象都是某個(gè)具體導(dǎo)出角色的實(shí)例。UML圖結(jié)構(gòu)分析練習(xí):工廠方法模式在農(nóng)場(chǎng)系統(tǒng)中的實(shí)現(xiàn)

系統(tǒng)優(yōu)化現(xiàn)在繼續(xù)考察農(nóng)場(chǎng)的管理系統(tǒng)。在本書(shū)的“簡(jiǎn)單工廠模式”一章里,討論了支持水果類作物的系統(tǒng)。在那個(gè)系統(tǒng)中,有一個(gè)全知全能的園丁角色(FriutGardaner),控制所有作物的種植、生長(zhǎng)和收獲。現(xiàn)在這個(gè)農(nóng)場(chǎng)的規(guī)模變大了,而同時(shí)發(fā)生的是管理更加專業(yè)化了。過(guò)去的全能人物沒(méi)有了,每一種農(nóng)作物都有專門(mén)的園丁管理,形成規(guī)?;蛯I(yè)化生產(chǎn)。系統(tǒng)設(shè)計(jì)取代了過(guò)去的全能角色的是一個(gè)抽象的園丁角色,這個(gè)角色規(guī)定出具體園丁角色需要實(shí)現(xiàn)的具體職能,而真正負(fù)責(zé)作物管理的則是負(fù)責(zé)各種作物的具體園丁角色。這一章仍然考慮前面所討論過(guò)的植物,包括葡萄(Grape)、草莓(Stmwberry)以及蘋(píng)果(Apple)等。專業(yè)化的管理要求有專門(mén)的園丁負(fù)責(zé)專門(mén)的水果,比如蘋(píng)果由“蘋(píng)果園丁”負(fù)責(zé),草莓有“草莓園丁”負(fù)責(zé),而葡萄由“葡萄園丁”負(fù)責(zé)。這些“蘋(píng)果園丁”、“草莓園丁”以及“葡萄園丁”都是實(shí)現(xiàn)了抽象的“水果園丁”接口的具體工廠類,而“水果園丁”則扮演抽象工廠角色。農(nóng)場(chǎng)系統(tǒng)的設(shè)計(jì)圖就如圖所示。源代碼請(qǐng)見(jiàn)配套電子版。第三節(jié) 抽象工廠(AbstractFactoryPattern)模式抽象工廠模式定義:為創(chuàng)建一組相關(guān)或相互依賴的對(duì)象提供一個(gè)接口,而且無(wú)需指定他們的具體類。每個(gè)模式都是針對(duì)一定問(wèn)題的解決方案。抽象工廠模式面對(duì)的問(wèn)題是多產(chǎn)品等級(jí)結(jié)構(gòu)的系統(tǒng)設(shè)計(jì)。抽象工廠模式的用意:抽象工廠模式可以向客戶端提供一個(gè)接口,使客戶端在不必指定產(chǎn)品具體類型的情況下,創(chuàng)建多個(gè)產(chǎn)品族中的產(chǎn)品對(duì)象。抽象工廠模式的起源抽象工廠模式的起源于用于創(chuàng)建分屬于不同操作系統(tǒng)的視窗構(gòu)件。如:命令按鍵(Button)與文字框(Text)都是視窗構(gòu)件,在UNIX操作系統(tǒng)的視窗環(huán)境和Windows操作系統(tǒng)的視窗環(huán)境中,這兩個(gè)構(gòu)件有不同的本地實(shí)現(xiàn),它們的細(xì)節(jié)有所不同。在學(xué)習(xí)抽象工廠具體實(shí)例之前,應(yīng)先明白兩個(gè)重要的概念:產(chǎn)品族和產(chǎn)品等級(jí)結(jié)構(gòu)。產(chǎn)品族windowsLinux按鈕按鈕文本框文本框產(chǎn)品等級(jí)結(jié)構(gòu)產(chǎn)品族:是指位于不同產(chǎn)品等級(jí)結(jié)構(gòu)中功能相關(guān)聯(lián)的產(chǎn)品組成的家族。抽象工廠模式所提供的一系列產(chǎn)品就組成一個(gè)產(chǎn)品族。如windows和Linux操作系統(tǒng)就是兩個(gè)不同的產(chǎn)品族。產(chǎn)品等級(jí)結(jié)構(gòu):工廠方法提供的一系列產(chǎn)品稱為一個(gè)等級(jí)結(jié)構(gòu)。windows和Linux操作系統(tǒng)中的“按鈕”、“文本框”等分別為一個(gè)產(chǎn)品等級(jí)結(jié)構(gòu)不同產(chǎn)品等級(jí)結(jié)構(gòu)產(chǎn)品對(duì)象的創(chuàng)建。工廠方法模式和抽象工廠模式的區(qū)別:如果工廠的產(chǎn)品全部屬于同一個(gè)等級(jí)結(jié)構(gòu),則屬于工廠方法模式;如果工廠的產(chǎn)品來(lái)自多個(gè)等級(jí)結(jié)構(gòu),則屬于抽象工廠模式。一般情況下,產(chǎn)品族和產(chǎn)品等級(jí)結(jié)構(gòu)的關(guān)系如圖所示。例子:女?huà)z造有性別人種話說(shuō)女?huà)z每日里采集黃土捏成人的形狀,然后放到八卦爐中燒制,最后放置到大地上生長(zhǎng),這活做多了,也是辛苦萬(wàn)分。那日這天便想到了一條妙計(jì):把繩子攪到泥水里,然后把沾滿泥水的繩子憑空一甩,甩出的泥點(diǎn)便是那人的形狀,然后放到八卦爐中燒制,最后放置到大地上生長(zhǎng)。這效率那是提高了不知多少倍,要不咱這地球哪來(lái)70億人。女?huà)z造萬(wàn)物系統(tǒng)里分陰、陽(yáng)兩個(gè)產(chǎn)品族,分別對(duì)應(yīng)兩條繩:陰繩和陽(yáng)繩,陰繩造出的是女人,陽(yáng)繩造出的是男人。這男女人還都分為黑人、白人和黃種人。讀者可以看出,女蝸造物用的是抽象工廠模式。女?huà)z造萬(wàn)物系統(tǒng)里產(chǎn)品等級(jí)結(jié)構(gòu)的“產(chǎn)品”有兩個(gè)劃分方法:一是按照“產(chǎn)品”是黑人、白人和黃種人來(lái)劃分,二是按照“產(chǎn)品”是男女、來(lái)劃分。女?huà)z的繩子按照陰、陽(yáng)劃分,產(chǎn)品則按照是黑人、白人和黃種人劃分。女?huà)z造萬(wàn)物系統(tǒng)里分陰、陽(yáng)兩個(gè)產(chǎn)品族和黑人、白人和黃種人三個(gè)產(chǎn)品等級(jí)結(jié)構(gòu)的類圖如圖所示。3.1 抽象工廠模式的結(jié)構(gòu)采用抽象工廠模式設(shè)計(jì)出的系統(tǒng)結(jié)構(gòu)圖如所示。從圖可以看出,抽象工廠模式涉及到以下的角色。抽象工廠模式的結(jié)構(gòu)抽象工廠模式涉及到以下的角色抽象工廠角色具體工廠類角色抽象產(chǎn)品角色具體產(chǎn)品角色抽象工廠模式系統(tǒng)結(jié)構(gòu)圖抽象工廠角色:是工廠方法模式的核心,與應(yīng)用系統(tǒng)的商業(yè)邏輯無(wú)關(guān)。用Java接口或抽象Java類實(shí)現(xiàn)。具體工廠類角色:該角色直接在客戶端調(diào)用下創(chuàng)建產(chǎn)品的實(shí)例。該角色含有選擇合適的產(chǎn)品對(duì)象的邏輯,這個(gè)邏輯是應(yīng)用系統(tǒng)的商業(yè)邏輯緊密相關(guān)。抽象產(chǎn)品角色:是工廠方法模式所創(chuàng)建的對(duì)象的父類。通常使用Java接口或者抽象Java類實(shí)現(xiàn)這一角色。具體產(chǎn)品角色:抽象工廠模式所創(chuàng)建的任何產(chǎn)品對(duì)象都是某一個(gè)具體產(chǎn)品類的實(shí)例。這是客戶端最終需要的東西,其內(nèi)部一定充滿了應(yīng)用系統(tǒng)的商業(yè)邏輯?!芭?huà)z造人”抽象工廠模式實(shí)現(xiàn)

抽象產(chǎn)品(AbstractProduct)角色,由Human、黑色人種、白色人種、黃色人種組成。具體產(chǎn)品(ConcreteProduct)角色由男性黑色人種、男性白色人種、男性黃色人種、女性黑色人種、女性白色人種、女性黃色人種組成。抽象工廠(AbstractFactory)角色由AbstractHumanFactory組成。具體工廠類(ConcreteFactory)角色由陰繩、陽(yáng)繩組成。女蝸造物抽象工廠模式UML結(jié)構(gòu)圖3.4 抽象工廠的優(yōu)缺點(diǎn):優(yōu)點(diǎn)分離接口和實(shí)現(xiàn)客戶端使用抽象工廠來(lái)創(chuàng)建需要的對(duì)象,而客戶端根本就不知道具體的實(shí)現(xiàn)是誰(shuí),客戶端只是面向產(chǎn)品的接口編程而已。也就是說(shuō),客戶端從具體的產(chǎn)品實(shí)現(xiàn)中解耦。使切換產(chǎn)品族變得容易因?yàn)橐粋€(gè)具體的工廠實(shí)現(xiàn)代表的是一個(gè)產(chǎn)品族,比如上面例子的從windows系列到Linux系列只需要切換一下具體工廠。缺點(diǎn)不太容易擴(kuò)展新的產(chǎn)品如果需要給整個(gè)產(chǎn)品族添加一個(gè)新的產(chǎn)品,那么就需要修改抽象工廠,這樣就會(huì)導(dǎo)致修改所有的工廠實(shí)現(xiàn)類。抽象工廠模式的示意性源代碼P45練習(xí):抽象工廠模式在農(nóng)場(chǎng)中的實(shí)現(xiàn)第四節(jié) 單例(Singleton)模式單例模式屬于對(duì)象模式單例模式:單例模式確保某一個(gè)類只有一個(gè)實(shí)例,而且自行實(shí)例化并向整個(gè)系統(tǒng)提供這個(gè)實(shí)例單例模式。單例模式只應(yīng)在有真正的“單一實(shí)例”的需求時(shí)才可使用。實(shí)用案例Windows操作系統(tǒng)都有一個(gè)回收站,回收站自行提供自己的實(shí)例,在整個(gè)系統(tǒng)中該回收站只能有一個(gè)實(shí)例,整個(gè)系統(tǒng)都使用這個(gè)惟一的實(shí)例。因此回收站是單例模式的應(yīng)用。打印機(jī)管理程序也是單例模式的應(yīng)用。4.1 單例模式的結(jié)構(gòu)單例模式的特點(diǎn):?jiǎn)卫愔荒苡幸粋€(gè)實(shí)例。單例類必須自己創(chuàng)建自己的唯一實(shí)例。單例類必須給所有其他對(duì)象提供這一實(shí)例。4.2 單例模式的類型單例模式分為三種類型:餓漢式、懶漢式和登記式。下面分別討論。4.2.1 餓漢式單例模式餓漢式指全局的單例實(shí)例在類裝載時(shí)構(gòu)建并初始化。優(yōu)點(diǎn)是速度快,不調(diào)用時(shí)也創(chuàng)建,餓漢式單例類被加載時(shí),靜態(tài)變量instance會(huì)被初始化,此時(shí)類的私有構(gòu)造函數(shù)會(huì)被調(diào)用。餓漢式其實(shí)是一種比較形象的稱謂。既然餓,那么在創(chuàng)建對(duì)象實(shí)例的時(shí)候就比較著急,餓了嘛,于是在裝載類的時(shí)候就創(chuàng)建對(duì)象實(shí)例。餓漢式是典型的空間換時(shí)間,當(dāng)類裝載的時(shí)候就會(huì)創(chuàng)建類的實(shí)例,不管你用不用,先創(chuàng)建出來(lái),然后每次調(diào)用的時(shí)候,就不需要再判斷,節(jié)省了運(yùn)行時(shí)間。餓漢式單例模式的實(shí)現(xiàn)4.2.2 懶漢式單例模式1懶漢式單例介紹懶漢式:指全局的單例實(shí)例在第一次被使用時(shí)構(gòu)建。延遲初始化。速度慢,調(diào)用時(shí)才創(chuàng)建。懶漢式其實(shí)是一種比較形象的稱謂。既然懶,那么在創(chuàng)建對(duì)象實(shí)例的時(shí)候就不著急。會(huì)一直等到馬上要使用對(duì)象實(shí)例的時(shí)候才會(huì)創(chuàng)建,懶人嘛,總是推脫不開(kāi)的時(shí)候才會(huì)真正去執(zhí)行工作,因此在裝載對(duì)象的時(shí)候不創(chuàng)建對(duì)象實(shí)例。

privatestaticLazySingletoninstance=null;懶漢式是典型的時(shí)間換空間。就是每次獲取實(shí)例都會(huì)進(jìn)行判斷,看是否需要?jiǎng)?chuàng)建實(shí)例,浪費(fèi)判斷的時(shí)間。當(dāng)然,如果一直沒(méi)有人使用的話,那就不會(huì)創(chuàng)建實(shí)例,則節(jié)約內(nèi)存空間,懶漢式單例模式的實(shí)現(xiàn)登記式單例模式登記式實(shí)際對(duì)一組單例模式進(jìn)行的維護(hù),主要是在數(shù)量上的擴(kuò)展,通過(guò)map我們把單例存進(jìn)去,這樣在調(diào)用時(shí),先判斷該單例是否已經(jīng)創(chuàng)建,是的話直接返回,不是的話創(chuàng)建一個(gè)登記到map中,再返回。對(duì)于數(shù)量又分為固定數(shù)量和不固定數(shù)量的。實(shí)現(xiàn)代碼可網(wǎng)上查詢,作為一個(gè)練習(xí)請(qǐng)大家完成。最安全的單例模式優(yōu)化程序如下所示:Python實(shí)現(xiàn)單例模式:第五節(jié) 多例(Multiton)模式多例模式屬于對(duì)象模式多例模式與單例模式一般性結(jié)構(gòu)對(duì)比示意圖:實(shí)用案例:支持多種語(yǔ)言的各類程序如網(wǎng)站等多例模式又劃分為有上限多例模式和無(wú)上限多例模式兩種,無(wú)上限多例模式和直接new一個(gè)對(duì)象沒(méi)什么差別,此處不再探究...有上限多例模式:實(shí)際上是單例模式的推廣,如果它的上限是1,那么就成了單例模式了。多例模式特點(diǎn):

1.多例類可以有多個(gè)實(shí)例

2.多例類必須自己創(chuàng)建自己的實(shí)例,并管理自己的實(shí)例,和向外界提供自己的實(shí)例一般采用聚集管理所有的實(shí)例多例模式結(jié)構(gòu)

多例模式與單例模式一般性結(jié)構(gòu)對(duì)比:多例模式有如下特點(diǎn):多例類可以有多個(gè)實(shí)例多例類必須自己創(chuàng)建自己的實(shí)例,并管理自己的實(shí)例,和向外界提供自己的實(shí)例多例類分為有上限多例類與無(wú)上限多例類。一個(gè)有上限的多例類已經(jīng)把實(shí)例的上限當(dāng)作邏輯的一部分,并建造到了多例類的內(nèi)部。例子一:兩個(gè)實(shí)例的多例類示意性程序代碼例子二:華爾街金融網(wǎng)站支持不同語(yǔ)言這是一個(gè)真實(shí)的面向全球消費(fèi)者的項(xiàng)目的一部分。按照項(xiàng)目計(jì)劃書(shū),這個(gè)網(wǎng)站系統(tǒng)是要由數(shù)據(jù)庫(kù)驅(qū)動(dòng)的,并且要支持19種不同的語(yǔ)言,而且在將來(lái)支持更多的語(yǔ)言。消費(fèi)者在登錄到系統(tǒng)時(shí)可以選擇自己所需要的語(yǔ)言,系統(tǒng)則根據(jù)用戶的選擇將網(wǎng)站的靜態(tài)文字和動(dòng)態(tài)文字全部轉(zhuǎn)換為用戶所選擇的語(yǔ)言。經(jīng)過(guò)討論,設(shè)計(jì)師們同意對(duì)靜態(tài)文字和動(dòng)態(tài)文字采取不同的解決方案:把所有的網(wǎng)頁(yè)交給翻譯公司對(duì)上面的靜態(tài)文字進(jìn)行翻譯。網(wǎng)頁(yè)上面的動(dòng)態(tài)內(nèi)容需要程序解決。貨幣代碼貨幣名稱貨幣尾數(shù)USDUS,Dollars2CNYChina,RenminbiYuan2EURFrance,Euro2JPYJapan,Yen0設(shè)計(jì)師們?cè)谶M(jìn)行了研究后發(fā)現(xiàn),他們需要解決的動(dòng)態(tài)文字的“翻譯”問(wèn)題,實(shí)際是將數(shù)據(jù)庫(kù)中的一些靜態(tài)或者半靜態(tài)的數(shù)據(jù)進(jìn)行“翻譯”。這里形成一個(gè)典型的數(shù)據(jù)表,如下表所示。程序中貨幣代碼不變,而貨幣名稱應(yīng)根據(jù)用戶所選擇的語(yǔ)言不同而不同。比如對(duì)中文讀者就應(yīng)當(dāng)翻譯成為如下表所示的樣子。貨幣代碼貨幣名稱貨幣尾數(shù)USD美國(guó)(美利堅(jiān)合眾國(guó)),美元2CNY中國(guó),人民幣元2EUR法國(guó),歐元2JPY日本,日元0對(duì)日文讀者就應(yīng)當(dāng)翻譯成為如表所示貨幣代碼貨幣名稱貨幣尾數(shù)USD米國(guó)(アメリカ合眾國(guó))、ドルで取引を終えた2CNY中國(guó)では、人民元だった2EURフランスで、ユーロ2JPY日本の円0對(duì)法文讀者就應(yīng)當(dāng)翻譯成為如表所示貨幣代碼貨幣名稱貨幣尾數(shù)USDAuxétats-Unis,dollars2CNYLaChine,leyuan2EURFrance,euros2JPYAuJapon,yen0這樣的表會(huì)在網(wǎng)頁(yè)上作為下拉菜單出現(xiàn),用戶看到的是貨幣名稱,而系統(tǒng)內(nèi)部使用的是貨幣代碼。系統(tǒng)的內(nèi)核可以是純英文的,在內(nèi)核外部增加-層負(fù)責(zé)語(yǔ)言翻譯工作。所謂內(nèi)核就是系統(tǒng)的模型,而翻譯層便是MVC模式中的視圖層的一部分。對(duì)多語(yǔ)言的支持屬于視圖功能。通過(guò)采用多例模式把對(duì)不同語(yǔ)言的選擇變成不同的參數(shù)并產(chǎn)生不同的對(duì)象再將結(jié)果傳回給讀者?!叭A爾街金融網(wǎng)站”結(jié)構(gòu)圖如圖所示代碼實(shí)現(xiàn)見(jiàn)配套電子版第六節(jié) 建造(Builder)模式建造模式是對(duì)象的創(chuàng)建模式。使用案例:電子郵件子系統(tǒng)電子郵件有多個(gè)組成成分組成,如發(fā)件人地址、收件人地址、主題、內(nèi)容、附件等多個(gè)部分。建造模式解析產(chǎn)品的內(nèi)部表象(internalrepresentation):一個(gè)產(chǎn)品常有不同的組成成分作為產(chǎn)品的零件,這些零件有可能是對(duì)象,也有可能不是對(duì)象,它們通常又叫做產(chǎn)品的內(nèi)部表象。建造模式可以將一個(gè)產(chǎn)品的內(nèi)部表象與產(chǎn)品的生產(chǎn)過(guò)程分割開(kāi)來(lái),從而可以使一個(gè)建造過(guò)程生成具有不同的內(nèi)部表象的產(chǎn)品對(duì)象。不同的產(chǎn)品可以有不同的內(nèi)部表象,也就是不同的零件。建造模式將產(chǎn)品的內(nèi)部表象和產(chǎn)品的生成過(guò)程分開(kāi)來(lái),從而使一個(gè)建造過(guò)程生成具有不同的內(nèi)部表象的產(chǎn)品對(duì)象。建造模式使得產(chǎn)品內(nèi)部表象可以獨(dú)立的變化,客戶不必知道產(chǎn)品內(nèi)部組成的細(xì)節(jié)。建造模式可以強(qiáng)制實(shí)行一種分步驟進(jìn)行的建造過(guò)程。6.1 建造模式的適用場(chǎng)景有些情況下,一個(gè)對(duì)象會(huì)有一些重要的性質(zhì),在它們沒(méi)有恰當(dāng)?shù)闹抵?,?duì)象不能作為一個(gè)完整的產(chǎn)品使用。比如,一個(gè)電子郵件有發(fā)件人地址、收件人地址、主題、內(nèi)容、附錄等部分,而在最起碼的收件人地址得到賦值之前,這個(gè)電子郵件不能發(fā)送。6.2 在什么情況下使用建造模式需要生成的產(chǎn)品對(duì)象有復(fù)雜的內(nèi)部結(jié)構(gòu),每一個(gè)內(nèi)部成分本身可以是對(duì)象,也可以僅僅是一個(gè)對(duì)象(即產(chǎn)品對(duì)象)的一個(gè)組成部分。需要生成的產(chǎn)品對(duì)象的屬性相互依賴。建造模式可以強(qiáng)制實(shí)行一種分步驟進(jìn)行的建造過(guò)程,因此,如果產(chǎn)品對(duì)象的一個(gè)屬性必須在另一個(gè)屬性被賦值之后才可以被賦值,使用建造模式是一個(gè)很好的設(shè)計(jì)思想。在對(duì)象創(chuàng)建過(guò)程中會(huì)使用到系統(tǒng)中的其他一些對(duì)象,這些對(duì)象在產(chǎn)品對(duì)象的創(chuàng)建過(guò)程中不易得到。6.3 建造模式的特點(diǎn)使用建造模式可以使客戶端不需要知道所生成的產(chǎn)品有哪些零件,每個(gè)產(chǎn)品的對(duì)應(yīng)零件彼此有何不同,是怎么建造出來(lái)的,以及怎么組成產(chǎn)品。建造模式利用一個(gè)導(dǎo)演者對(duì)象和具體建造者對(duì)象一個(gè)個(gè)地建造出所有的零件,從而建造出完整的產(chǎn)品對(duì)象。建造者模式將產(chǎn)品結(jié)構(gòu)和產(chǎn)品零件的建造過(guò)程對(duì)客戶端隱藏起來(lái),把對(duì)建造過(guò)程進(jìn)行指揮的責(zé)任和具體建造者零件的責(zé)任分割開(kāi)來(lái),達(dá)到責(zé)任劃分和封裝的目的。6.4 建造模式的結(jié)構(gòu)不管如何變化,建造模式都存在這么兩個(gè)部分部件構(gòu)造和產(chǎn)品裝配,整體構(gòu)建的算法。認(rèn)識(shí)這點(diǎn)是很重要的,因?yàn)樵诮ㄔ炷J街校瑥?qiáng)調(diào)的是固定整體構(gòu)建的算法,而靈活擴(kuò)展和切換部件的具體構(gòu)造和產(chǎn)品裝配的方式。建造模式的示意性結(jié)構(gòu)圖在這個(gè)示意性的系統(tǒng)里,最終產(chǎn)品Product只有兩個(gè)零件,即part1和part2。相應(yīng)的建造方法也有兩個(gè):buildPart1()和buildPart2()同時(shí)可以看出本模式涉及到四個(gè)角色,它們分別是:抽象建造者角色:給出一個(gè)抽象接口,以規(guī)范產(chǎn)品對(duì)象的各個(gè)組成成分的建造。具體建造者角色:在應(yīng)用程序調(diào)用下創(chuàng)建產(chǎn)品的實(shí)例。導(dǎo)演者角色:調(diào)用具體建造者角色以創(chuàng)建產(chǎn)品對(duì)象。產(chǎn)品角色:建造的各種復(fù)雜對(duì)象,這些產(chǎn)品類并不一定有共同的接口,而完全可以是不相關(guān)聯(lián)的。一般來(lái)說(shuō),每有一個(gè)產(chǎn)品類,就有一個(gè)相應(yīng)的具體建造者類。這些產(chǎn)品應(yīng)當(dāng)有同樣數(shù)目的零件,而每有一個(gè)零件就相應(yīng)地在所有的建造者角色里有一個(gè)建造方法。建造模式的實(shí)現(xiàn)練習(xí):構(gòu)建電子郵件信箱發(fā)送郵件模塊系統(tǒng)需求分析:假設(shè)有一個(gè)系統(tǒng)需要定期向用戶電子郵件信箱發(fā)送電子雜志。用戶可以通過(guò)網(wǎng)頁(yè)訂閱電子雜志,也可以通過(guò)網(wǎng)頁(yè)結(jié)束訂閱。當(dāng)客戶開(kāi)始訂閱時(shí),系統(tǒng)發(fā)送一個(gè)電子郵件表示歡迎,當(dāng)客戶結(jié)束訂閱時(shí),系統(tǒng)發(fā)送一個(gè)電子郵件表示歡送。本例就是這個(gè)系統(tǒng)負(fù)責(zé)發(fā)送“歡迎”和“歡送”郵件的模塊。系統(tǒng)設(shè)計(jì)在本例中,產(chǎn)品類就是發(fā)給某個(gè)客戶的“歡迎”和“歡送”郵件,如圖所示。雖然在這個(gè)例子里面各個(gè)產(chǎn)品類均有一個(gè)共同的接口,但這僅僅是本例子特有的,并不代表建造模式的特點(diǎn)。建造模式可以應(yīng)用到具有完全不同接口的產(chǎn)品類上。大多數(shù)情況下是不知道最終構(gòu)建出來(lái)的產(chǎn)品是什么樣的,所以在標(biāo)準(zhǔn)的建造模式里面,一般是不需要對(duì)產(chǎn)品定義抽象接口的,因?yàn)樽罱K構(gòu)造的產(chǎn)品千差萬(wàn)別,給這些產(chǎn)品定義公共接口幾乎是沒(méi)有意義的。系統(tǒng)采用建造模式設(shè)計(jì)的結(jié)構(gòu)圖這個(gè)系統(tǒng)含有客戶端(Client)、導(dǎo)演者(Director)、抽象建造者(Builder)、具體建造者(WelcomeBuilder和GoodbyeBuilder)、產(chǎn)品(WelcomeMessage和GoodbyeMessage)等角色。第七節(jié) 原型(Prototype)模式原型模式屬于對(duì)象的創(chuàng)建模式。通過(guò)給出一個(gè)原型對(duì)象來(lái)指明所有創(chuàng)建的對(duì)象的類型,然后用復(fù)制這個(gè)原型對(duì)象的辦法創(chuàng)建出更多同類型的對(duì)象。這就是原型模式的用意。實(shí)用案例:需要快速創(chuàng)建同類對(duì)象的需求使用原型模式的好處使用原型模式創(chuàng)建對(duì)象比直接new一個(gè)對(duì)象在性能上要好的多,因?yàn)镺bject類的clone方法是一個(gè)本地方法,它直接操作內(nèi)存中的二進(jìn)制流,特別是復(fù)制大對(duì)象時(shí),性能的差別非常明顯。簡(jiǎn)化對(duì)象的創(chuàng)建,使得創(chuàng)建對(duì)象就像我們?cè)诰庉嬑臋n時(shí)的復(fù)制粘貼一樣簡(jiǎn)單。因?yàn)橐陨蟽?yōu)點(diǎn),在需要重復(fù)地創(chuàng)建相似對(duì)象時(shí)可以考慮使用原型模式。例子:孫悟空身外身法術(shù)從孫大圣的手段談起孫悟空在與黃風(fēng)怪的戰(zhàn)斗中,“使一個(gè)身外身的手段:把毫毛揪下一把,用口嚼得粉碎,望上一噴,叫聲‘變’,變有百十個(gè)行者,都是一樣打扮,各執(zhí)一根鐵棒,把那怪圍在空中?!边@個(gè)例子中,孫悟空可以根據(jù)自己的形象,復(fù)制出很多“身外之身”來(lái)。例子總結(jié)老孫的這種身外身的手段在面向?qū)ο蟮脑O(shè)計(jì)領(lǐng)域里叫做原型(Prototype)模式。原型模式通過(guò)給出一個(gè)原型對(duì)象來(lái)指明所要?jiǎng)?chuàng)建的對(duì)象的類型,然后用復(fù)制這個(gè)原型對(duì)象的方法創(chuàng)建出更多同類型的對(duì)象。原始模型模式允許動(dòng)態(tài)的增加或減少產(chǎn)品類,產(chǎn)品類不需要非得有任何事先確定的等級(jí)結(jié)構(gòu),原始模型模式適用于任何的等級(jí)結(jié)構(gòu)。原始模型模式缺點(diǎn)是每一個(gè)類都必須配備一個(gè)克隆方法。原型模式的結(jié)構(gòu)原型模式要求對(duì)象實(shí)現(xiàn)一個(gè)可以“克隆”自身的接口,這樣就可以通過(guò)復(fù)制一個(gè)實(shí)例對(duì)象本身來(lái)創(chuàng)建一個(gè)新的實(shí)例。這樣一來(lái),通過(guò)原型實(shí)例創(chuàng)建新的對(duì)象,就不再需要關(guān)心這個(gè)實(shí)例本身的類型,只要實(shí)現(xiàn)了克隆自身的方法,就可以通過(guò)這個(gè)方法來(lái)獲取新的對(duì)象,而無(wú)須再去通過(guò)new來(lái)創(chuàng)建。原型模式有兩種表現(xiàn)形式:(1)簡(jiǎn)單形式(2)登記形式這兩種表現(xiàn)形式僅僅是原型模式的不同實(shí)現(xiàn)。7.1

簡(jiǎn)單形式的原型模式簡(jiǎn)單形式的原型模式結(jié)構(gòu)圖如圖所示。簡(jiǎn)單形式的原型模式涉及到三個(gè)角色:(1)客戶(Client)角色:客戶類提出創(chuàng)建對(duì)象的請(qǐng)求。(2)抽象原型(Prototype)角色:這是一個(gè)抽象角色,通常由一個(gè)Java接口或Java抽象類實(shí)現(xiàn)。此角色給出所有的具體原型類所需的接口。(3)具體原型(ConcretePrototype)角色:被復(fù)制的對(duì)象。此角色需要實(shí)現(xiàn)抽象的原型角色所要求的接口。簡(jiǎn)單形式的原型模式的示意性源代碼7.2

登記形式的原型模式登記形式的原型模式結(jié)構(gòu)圖如圖2-7-2所示。作為原型模式的第二種形式多了一個(gè)原型管理器(PrototypeManager)角色,該角色的作用是:創(chuàng)建具體原型類的對(duì)象,并記錄每一個(gè)被創(chuàng)建的對(duì)象。登記形式的原型模式實(shí)現(xiàn)一登記形式的原型模式實(shí)現(xiàn)二登記形式的原型模式實(shí)現(xiàn)三程序運(yùn)行結(jié)果客戶端測(cè)試程序中由于“有人注銷了這個(gè)原型”,所以在調(diào)用PrototypeManager.getPrototype("p1")方法時(shí)拋出“newException("您希望獲取的原型還沒(méi)有注冊(cè)或已被銷毀")”。7.3

兩種形式的比較簡(jiǎn)單形式和登記形式的原型模式各有其長(zhǎng)處和短處。如果需要?jiǎng)?chuàng)建的原型對(duì)象數(shù)目較少而且比較固定的話,可以采取第一種形式。在這種情況下,原型對(duì)象的引用可以由客戶端自己保存。如果要?jiǎng)?chuàng)建的原型對(duì)象數(shù)目不固定的話,可以采取第二種形式。這種情況下,客戶端不保存對(duì)原型對(duì)象的引用,這個(gè)任務(wù)被交給管理員對(duì)象。在復(fù)制一個(gè)原型對(duì)象之前,客戶端可以查看管理員對(duì)象是否已經(jīng)有一個(gè)滿足要求的原型對(duì)象。如果有,可以直接從管理員類取得這個(gè)對(duì)象引用;如果沒(méi)有,客戶端就需要自行復(fù)制此原型對(duì)象。練習(xí):“孫悟空身外身法術(shù)”淺復(fù)制實(shí)現(xiàn)孫大圣的變身本領(lǐng)如果在Java語(yǔ)言里使用原型模式來(lái)實(shí)現(xiàn)的話,會(huì)怎么樣呢?首先,齊天大圣(TheGreatestSage)即TheGreatestSage類扮演客戶角色。齊天大圣持有一個(gè)猢猻(Monkey)的實(shí)例,而猢猻就是大圣本尊。Monkey類具有繼承自java.lang.Object的clone()方法,因此,可以通過(guò)調(diào)用這個(gè)克隆方法來(lái)復(fù)制一個(gè)Monkey實(shí)例。結(jié)構(gòu)設(shè)計(jì)圖如圖所示?!皩O悟空身外身法術(shù)”淺復(fù)制實(shí)現(xiàn)分析:首先,復(fù)制的大圣本尊具有和原始的大圣本尊對(duì)象一樣的birthDate,而本尊對(duì)象不相等,這表明他們二者是克隆關(guān)系;其次,復(fù)制的大圣本尊所持有的金箍棒和原始的大圣本尊所持有的金箍棒為同一個(gè)對(duì)象。這表明二者所持有的金箍棒根本是一根,而不是兩根。正如前面所述,繼承自java.lang.Object類的clone()方法是淺克隆。換言之,齊天大圣的所有化身所持有的金箍棒引用全都是指向一個(gè)對(duì)象的,這與《西游記》中的描寫(xiě)不一致。要糾正這一點(diǎn),就需要考慮使用深克隆。深復(fù)制實(shí)現(xiàn)為做到深度克隆,所有需要復(fù)制的對(duì)象都需要實(shí)現(xiàn)java.io.Serializable接口。結(jié)構(gòu)設(shè)計(jì)圖如圖所示。第三章結(jié)構(gòu)模式(StructuralPattern)第三章結(jié)構(gòu)模式(StructuralPattern)結(jié)構(gòu)模式描述如何將類或者對(duì)象結(jié)合在一起形成更大的結(jié)構(gòu),結(jié)構(gòu)模式描述兩種不同的東西:類與類的對(duì)象。根據(jù)這一不同,結(jié)構(gòu)模式可以分為類的結(jié)構(gòu)模式和對(duì)象的結(jié)構(gòu)模式兩種:類的結(jié)構(gòu)模式:類的結(jié)構(gòu)模式使用繼承來(lái)把類,接口等組合在一起,以形成更大的結(jié)構(gòu)。當(dāng)一個(gè)類從父類繼承并實(shí)現(xiàn)某接口時(shí),這個(gè)新的類就把父類的結(jié)構(gòu)和接口的結(jié)構(gòu)結(jié)合起來(lái)。類的結(jié)構(gòu)模式是靜態(tài)的。一個(gè)類的結(jié)構(gòu)模式的典型例子,就是類形式的適配器模式。對(duì)象的結(jié)構(gòu)模式:對(duì)象的結(jié)構(gòu)模式描述怎樣把各種不同類型的對(duì)象組合在一起,以實(shí)現(xiàn)新的功能的方法。對(duì)象的結(jié)構(gòu)模式是動(dòng)態(tài)的。本章將要介紹的結(jié)構(gòu)型模式包括八種:適配器模式、缺省適配器模式、合成模式、裝飾模式、代理模式、享元模式、門(mén)面模式、橋梁模式。第一節(jié)適配器(Adapter)模式適配器模式把一個(gè)類的接口變換成客戶端所期待的另一種接口,從而使原本因接口不匹配而無(wú)法在一起工作的兩個(gè)類能夠在一起工作。適配類可以根據(jù)參數(shù)返還一個(gè)合適的實(shí)例給客戶端。實(shí)用案例:版本升級(jí)。例子:電源適配器(變壓器)變壓器的功能是把一種電壓值變換為另一種電壓值。美國(guó)標(biāo)準(zhǔn)電器在中國(guó)大陸用時(shí),因美國(guó)的生活用電電壓是110V,而中國(guó)的是220V。如果要在中國(guó)使用美標(biāo)電器,就必須有一個(gè)能把220V電壓轉(zhuǎn)換成110V電壓的變壓器。而這正像是本模式所做的事,因此該模式也常被稱為變壓器模式。筆記本電腦的插頭一般都是三相的,即除了正、負(fù)極外,還有一個(gè)零極。而有些地方的電源插座卻只有兩極,沒(méi)有零極。電源插座與筆記本電腦的電源插頭不匹配使得筆記本電腦無(wú)法使用。這時(shí)候一個(gè)三相到兩相的轉(zhuǎn)換器(適配器)就能解決此問(wèn)題。1.1適配器模式的結(jié)構(gòu)適配器模式有類的適配器模式和對(duì)象的適配器模式兩種不同的形式。

1.1.1類適配器模式類的適配器模式把適配的類的API轉(zhuǎn)換成為目標(biāo)類的API。其結(jié)構(gòu)圖如圖所示。目標(biāo)(Target)角色源(Adapee)角色適配器(Adaper)角色該模式所涉及的角色目標(biāo)(Target)角色:這就是所期待得到的接口。注意:由于這里討論的是類適配器模式,因此目標(biāo)不可以是類。源(Adapee)角色:現(xiàn)在需要適配的接口。適配器(Adaper)角色:適配器類是本模式的核心。適配器把源接口轉(zhuǎn)換成目標(biāo)接口。顯然,這一角色不可以是接口,而必須是具體類。類適配器模式實(shí)現(xiàn)1.1.2對(duì)象適配器模式與類的適配器模式一樣,對(duì)象的適配器模式把被適配的類的API轉(zhuǎn)換成為目標(biāo)類的API,與類的適配器模式不同的是,對(duì)象的適配器模式不是使用繼承關(guān)系連接到Adaptee類,而是使用委派關(guān)系連接到Adaptee類。對(duì)象適配器模式的結(jié)構(gòu)圖如圖所示。目標(biāo)(Target)角色源(Adapee)角色適配器(Adaper)角色從上圖可以看出,Adaptee類并沒(méi)有sampleOperation2()方法,而客戶端則期待這個(gè)方法。為使客戶端能夠使用Adaptee類,需要提供一個(gè)包裝類Adapter,它包裝了一個(gè)Adaptee的實(shí)例,從而能夠把Adaptee的API與Target類的API銜接起來(lái)。Adapter與Adaptee是委派關(guān)系,這決定了適配器模式是對(duì)象的。對(duì)象適配器模式實(shí)現(xiàn)類適配器和對(duì)象適配器的權(quán)衡一類適配器使用對(duì)象繼承的方式,是靜態(tài)的定義方式;而對(duì)象適配器使用對(duì)象組合的方式,是動(dòng)態(tài)組合的方式。對(duì)于類適配器,由于適配器直接繼承了Adaptee,使得適配器不能和Adaptee的子類一起工作,因?yàn)槔^承是靜態(tài)的關(guān)系,當(dāng)適配器繼承了Adaptee后,就不可能再去處理Adaptee的子類了。對(duì)于對(duì)象適配器,一個(gè)適配器可以把多種不同的源適配到同一個(gè)目標(biāo)。換言之,同一個(gè)適配器可以把源類和它的子類都適配到目標(biāo)接口。因?yàn)閷?duì)象適配器采用的是對(duì)象組合的關(guān)系,只要對(duì)象類型正確,是不是子類都無(wú)所謂。類適配器和對(duì)象適配器的權(quán)衡二對(duì)于類適配器,適配器可以重定義Adaptee的部分行為,相當(dāng)于子類覆蓋父類的部分實(shí)現(xiàn)方法。對(duì)于對(duì)象適配器,要重定義Adaptee的行為比較困難,這種情況下,需要定義Adaptee的子類來(lái)實(shí)現(xiàn)重定義,然后讓適配器組合子類。雖然重定義Adaptee的行為比較困難,但是想要增加一些新的行為則方便的很,而且新增加的行為可同時(shí)適用于所有的源。對(duì)于類適配器,僅僅引入了一個(gè)對(duì)象,并不需要額外的引用來(lái)間接得到Adaptee。對(duì)于對(duì)象適配器,需要額外的引用來(lái)間接得到Adaptee。建議盡量使用對(duì)象適配器的實(shí)現(xiàn)方式,多用合成/聚合、少用繼承。當(dāng)然,具體問(wèn)題具體分析,根據(jù)需要來(lái)選用實(shí)現(xiàn)方式,最適合的才是最好的。例子:“電源適配器”實(shí)現(xiàn)JDK中遍歷方法升級(jí):JDK1.2有Iterator接口,老版本是Enumeration接口,效率低,但應(yīng)用很廣,為減少修改工作量,采用適配器模式。如下所示:Iterator接口實(shí)現(xiàn)方式Listlist=newArrayList();for(Iteratorit=list.iterator();it.hasNext();){

System.out.println(it.next());}Enumeration接口實(shí)現(xiàn)方式Vectorv=newVector();for(Enumerationenum=v.elements();enum.hasMoreElements();){System.out.println(enum.nextElement(););}采用適配器模式importjava.util.ArrayList;importjava.util.Enumeration;importjava.util.Iterator;importjava.util.List;publicclassNewEnumerationimplementsEnumeration{Iteratorit;

publicNewEnumeration(Iteratorit){

this.it=it;}

publicboolean

hasMoreElements(){returnit.hasNext();

}

publicObjectnextElement(){returnit.next();

}

publicstaticvoidmain(String[]args){

Listlist=newArrayList();

list.add("a");list.add("b");list.add("C");

for(Enumeratione=newNewEnumeration(list.iterator());e.hasMoreElements();){

System.out.println(e.nextElement());

}

}}1.3適配器模式的優(yōu)缺點(diǎn)優(yōu)點(diǎn)更好的復(fù)用性系統(tǒng)需要使用現(xiàn)有的類,而此類的接口不符合系統(tǒng)的需要。那么通過(guò)適配器模式就可以讓這些功能得到更好的復(fù)用。更好的擴(kuò)展性在實(shí)現(xiàn)適配器功能的時(shí)候,可以調(diào)用自己開(kāi)發(fā)的功能,從而自然地?cái)U(kuò)展系統(tǒng)的功能。缺點(diǎn)過(guò)多的使用適配器,會(huì)讓系統(tǒng)非常零亂,不易整體進(jìn)行把握。比如,明明看到調(diào)用的是A接口,其實(shí)內(nèi)部被適配成了B接口的實(shí)現(xiàn),一個(gè)系統(tǒng)如果太多出現(xiàn)這種情況,無(wú)異于一場(chǎng)災(zāi)難。因此如果不是很有必要,可以不使用適配器,而是直接對(duì)系統(tǒng)進(jìn)行重構(gòu)。練習(xí):利用適配器模式指方為圓中國(guó)古代有趙高指鹿為馬的故事,而《楚辭·九辯》中說(shuō):“圜鑿而方柄兮,吾固知其齟齬而難入?!辈徽撌侵嘎篂轳R還是指方為圓,都需要適配器模式來(lái)實(shí)現(xiàn)。本節(jié)就用適配器模式這個(gè)魔術(shù)手指實(shí)現(xiàn)“指方為圓”。適配器模式在本例子中的類圖如圖所示。目標(biāo)(Target)角色源(Adapee)角色適配器(Adaper)角色運(yùn)行結(jié)果如下:第二節(jié)缺省適配(DefaultAdapter)模式缺省適配模式為一個(gè)接口提供缺省實(shí)現(xiàn),這樣子類型可以從這個(gè)缺省實(shí)現(xiàn)進(jìn)行擴(kuò)展,而不必從原有接口進(jìn)行擴(kuò)展。應(yīng)用:當(dāng)原接口中定義的方法太多,而其中大部分又不被需要時(shí),這種模式非常實(shí)用。由缺省適配器類直接實(shí)現(xiàn)接口,并為所有方法提供缺省的空實(shí)現(xiàn)。用戶類就只需要繼承缺省適配器類,并實(shí)現(xiàn)感興趣的方法就行了。2.1缺省適配模式的結(jié)構(gòu)在很多情況下,必須讓一個(gè)具體類實(shí)現(xiàn)某一個(gè)接口,但是這個(gè)類又用不到接口所規(guī)定的所有的方法。通常的處理方法是,這個(gè)具體類要實(shí)現(xiàn)所有的方法,那些有用的方法要有實(shí)現(xiàn),那些沒(méi)有用的方法也要有空的、平庸的實(shí)現(xiàn)。這些空的方法是一種浪費(fèi),有時(shí)也是一種混亂。除非看過(guò)這些空方法的代碼,程序員可能會(huì)以為這些方法不是空的。即便他知道其中有一些方法是空的,也不一定知道哪些方法是空的,哪些方法不是空的,除非看過(guò)這些方法的源代碼或是文檔。例子:魯智深受戒和尚要做什么呢?吃齋、念經(jīng)、打坐、撞鐘、習(xí)武等。按照常規(guī)的做法魯智深想成為一個(gè)和尚應(yīng)該如圖所示。魯智深除了會(huì)武術(shù)外其他都不會(huì),只能實(shí)現(xiàn)getName()和習(xí)武()方法,而不能實(shí)現(xiàn)任何其他的方法。因此,它根本就通不過(guò)Java語(yǔ)言編譯器。魯智深類只有實(shí)現(xiàn)和尚接口的所有的方法才可以通過(guò)Java語(yǔ)言編譯器,但是這樣一來(lái)魯智深就不再是魯智深了。以史為鑒,可以知天下。研究一下幾百年前魯智深是怎么剃度成和尚的,會(huì)對(duì)學(xué)習(xí)Java編程有很大的啟發(fā)。當(dāng)初魯達(dá)剃度,眾僧說(shuō):“此人形容丑惡、相貌兇頑,不可剃度他”。但是長(zhǎng)老卻說(shuō):“此人上應(yīng)天星、心地剛直。雖然時(shí)下兇頑,命中駁雜,久后卻得清凈。證果非凡,汝等皆不及他?!比鐖D示原來(lái)如此!看來(lái)只要這里應(yīng)上一個(gè)天星的話,問(wèn)題就解決了!使用面向?qū)ο蟮恼Z(yǔ)言來(lái)說(shuō),“應(yīng)”者,實(shí)現(xiàn)也;“天星”者,抽象類也。采用缺省適配模式后魯智深也就成為了和尚,設(shè)計(jì)如圖所示。魯智深成為和尚魯智深想成為和尚缺省適配模式結(jié)構(gòu)設(shè)計(jì)圖魯智深成為和尚是一種“平庸”化的適配器模式。設(shè)計(jì)一個(gè)抽象的適配器類實(shí)現(xiàn)接口,此抽象類要給接口所要求的每種方法都提供一個(gè)空方法。就像幫助了魯智深的“上應(yīng)天星”類一樣,此抽象類可以使它的具體子類免于被迫實(shí)現(xiàn)空的方法。缺省適配模式是什么缺省適配模式示意性源代碼接口AbstractService定義三個(gè)方法提供了平庸的實(shí)現(xiàn)選擇它所需要的方法實(shí)現(xiàn),不必理會(huì)其他不需要的方法。繼承自抽象類ServiceAdapter的具體類Service抽象適配器ServiceAdapter類適配器模式的用意是要改變?cè)吹慕涌?,以便于目?biāo)接口相容。缺省適配的用意稍有不同,它是為了方便建立一個(gè)不平庸的適配器類而提供的一種平庸實(shí)現(xiàn)。在任何時(shí)候,如果不準(zhǔn)備實(shí)現(xiàn)一個(gè)接口的所有方法時(shí),就可以使用“缺省適配模式”制造一個(gè)抽象類,給出所有方法的平庸的具體實(shí)現(xiàn)。這樣,從這個(gè)抽象類再繼承下去的子類就不必實(shí)現(xiàn)所有的方法了。2.2“魯智深受戒”實(shí)現(xiàn)1、和尚接口,給出所有的和尚都需要實(shí)現(xiàn)的方法:2、抽象類作為適配器類:

3、魯智深類繼承抽象類“天星”客戶端測(cè)試程序程序運(yùn)行結(jié)果如下:魯智深借助于適配器模式達(dá)到了剃度的目的。此適配器類實(shí)現(xiàn)了和尚接口所要求的所有方法。但是與通常的適配器模式不同的是,此適配器類給出的所有的方法的實(shí)現(xiàn)都是“平庸”的。這種“平庸化”的適配器模式稱作缺省適配模式。第三節(jié)合成(Composite)模式合成模式屬于對(duì)象的結(jié)構(gòu)模式,又稱為“部分-整體”模式。實(shí)用案例:文件和文件夾的管理第三節(jié)合成(Composite)模式合成模式將對(duì)象組織到樹(shù)結(jié)構(gòu)中,可以用來(lái)描述整體與部分的關(guān)系。合成模式可以使客戶端將單純?cè)嘏c復(fù)合元素同等看待。合成模式就是一個(gè)處理對(duì)象的樹(shù)結(jié)構(gòu)的模式。3.1合成模式結(jié)構(gòu)合成模式可以不提供父對(duì)象的管理方法,但是合成模式必須在合適的地方提供對(duì)子對(duì)象的管理方法,諸如:add()、remove()、以及getChild()等。合成模式的實(shí)現(xiàn)根據(jù)所實(shí)現(xiàn)接口的區(qū)別分為兩種形式,分別稱為安全式和透明式。3.1.1安全式合成模式的結(jié)構(gòu)安全模式的合成模式要求管理聚集的方法只出現(xiàn)在樹(shù)枝構(gòu)件類中,而不出現(xiàn)在樹(shù)葉構(gòu)件類中。安全式合成模式結(jié)構(gòu)圖如圖所示。安全式合成模式涉及三個(gè)角色抽象構(gòu)件(Component)角色樹(shù)葉構(gòu)件(Leaf)角色樹(shù)枝構(gòu)件(Composite)角色安全式合成模式三個(gè)角色抽象構(gòu)件(Component)角色:給參加組合的對(duì)象定義出公共的接口及其默認(rèn)行為,管理子對(duì)象的方法由樹(shù)枝構(gòu)件角色給出。樹(shù)葉構(gòu)件(Leaf)角色:沒(méi)有下級(jí)子對(duì)象,定義參加組合的原始對(duì)象的行為。樹(shù)枝構(gòu)件(Composite)角色:代表參加組合的有下級(jí)子對(duì)象的對(duì)象。樹(shù)枝構(gòu)件類給出所有的管理子對(duì)象的方法,如add()、remove()以及getChild()。安全式合成模式實(shí)現(xiàn)抽象構(gòu)件角色類安全式合成模式實(shí)現(xiàn)樹(shù)枝構(gòu)件角色類安全式合成模式實(shí)現(xiàn)樹(shù)葉構(gòu)件角色類安全式合成模式實(shí)現(xiàn)客戶端:程序運(yùn)行結(jié)果圖3.1.2透明式合成模式的結(jié)構(gòu)與安全式的合成模式不同的是,透明式的合成模式要求所有的具體構(gòu)件類,不論樹(shù)枝構(gòu)件還是樹(shù)葉構(gòu)件,均符合一個(gè)固定接口。結(jié)構(gòu)圖如圖所示??梢钥闯?,客戶端無(wú)需再區(qū)分操作的是樹(shù)枝對(duì)象(Composite)還是樹(shù)葉對(duì)象(Leaf)了;對(duì)于客戶端而言,操作的都是Component對(duì)象。透明式合成模式的實(shí)現(xiàn)抽象構(gòu)件角色類透明式合成模式的實(shí)現(xiàn)樹(shù)枝構(gòu)件角色類透明式合成模式的實(shí)現(xiàn)樹(shù)葉構(gòu)件角色類透明式合成模式的實(shí)現(xiàn)客戶端類3.3兩種實(shí)現(xiàn)方法的選擇這里所說(shuō)的安全性合成模式是指:從客戶端使用合成模式上看是否更安全,如果是安全的,那么就不會(huì)有發(fā)生誤操作的可能,能訪問(wèn)的方法都是被支持的。這里所說(shuō)的透明性合成模式是指:從客戶端使用合成模式上,是否需要區(qū)分到底是“樹(shù)枝對(duì)象”還是“樹(shù)葉對(duì)象”。如果是透明的,那就不用區(qū)分,對(duì)于客戶而言,都是Compoent對(duì)象,具體的類型對(duì)于客戶端而言是透明的,是無(wú)須關(guān)心的。對(duì)于合成模式而言,在安全性和透明性上,會(huì)更看重透明性,畢竟合成模式的目的是:讓客戶端不再區(qū)分操作的是樹(shù)枝對(duì)象還是樹(shù)葉對(duì)象,而是以一個(gè)統(tǒng)一的方式來(lái)操作

溫馨提示

  • 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒(méi)有圖紙預(yù)覽就沒(méi)有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫(kù)網(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)論