


版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報或認(rèn)領(lǐng)
文檔簡介
1、第14章 原則和模式為了有效地使用像 UML 這樣的設(shè)計表示法,只是掌握不同類型的語法和語義是不夠 的。形式表示法的可用性并不能保證會很好地使用這些表示法, 好的設(shè)計和不好的設(shè)計都可 以用 UML 表達(dá)。當(dāng)然,好的設(shè)計和不好的設(shè)計之間的不同特征是很難用純粹的形式詞語描述的,很可 能在任何完備程度上也不可能做到。 推薦某些方法, 保證設(shè)計師做出的設(shè)計一定是好的設(shè)計 也非常困難。 然而現(xiàn)在已經(jīng)有了面向?qū)ο蠼:驮O(shè)計的大量經(jīng)驗, 使我們可以更好地理解怎 樣做可能會使設(shè)計成功或不成功。面向?qū)ο笤O(shè)計師們積累的經(jīng)驗可以分為兩個不同的范疇。 一類是一些廣泛認(rèn)可的高級設(shè) 計原則。 這些原則描述了設(shè)計應(yīng)當(dāng)具有的
2、或者應(yīng)當(dāng)避開的值得注意的性質(zhì)。 對于依據(jù)這些原 則的基本原理所指出的設(shè)計特征建立的系統(tǒng),經(jīng)驗證實,可以預(yù)知其結(jié)果。這些高級原則是很重要的,但是對于試圖針對具體應(yīng)用建模的設(shè)計師,卻幾乎不能提 供可操作的指導(dǎo)。 針對這些情況, 需要一些記實性地描述不同種類的設(shè)計知識, 這些知識更 關(guān)注特定的問題和解決這些問題的策略。 當(dāng)前在 設(shè)計模式 方面的工作就是通過識別共同的建 模問題,并對這些問題提供經(jīng)過驗證的解決方案的方式,滿足這種需要。本章將討論若干已知的廣泛接受的面向?qū)ο笤O(shè)計的原則, 然后介紹設(shè)計模式的概念。 模 式的用法將通過考慮對本書前面章節(jié)中出現(xiàn)的程序的修改予以介紹。14.1 開-閉原則開(放)
3、 -(封)閉原則是 Bertrand Meyer 1988 年在他的有影響的著作 面向?qū)ο蟮能浖?gòu)造 中闡述的。 這個原則關(guān)注的是系統(tǒng)內(nèi)部改變的影響, 特別是最大限度地使模塊免受 它所使用的其它模塊改變的影響的辦法??紤]系統(tǒng)中一個模塊使用另一個模塊提供的服務(wù)的情況。通常稱前一個模塊為 客戶(client ),后一個模塊為 供應(yīng)者 ( supplier )。雖然這兩個概念有更廣泛的使用,本章詳細(xì)考 慮的只是用于類之間的關(guān)系, 即可以用 UML 的使用依賴建模的關(guān)系。 圖 14.1 表示了這種情 況。圖14.1客戶類和供應(yīng)者類之間的使用依賴如果一個模塊不受進(jìn)一步改變的影響,則稱此模塊是關(guān)閉的。這意
4、味著,客戶模塊可以放心地使用該模塊而無須擔(dān)心該模塊的改變會使客戶模塊也必須改變。關(guān)閉一個模塊是有好處的,因為這意味著這個模塊以后可以作為系統(tǒng)的一個穩(wěn)定的構(gòu)件使用,它不再受到進(jìn)一步改變的影響,而這種改變將會反過來影響到設(shè)計的其他部分。如果一個模塊仍然是可以擴展的,用Meyer的詞語,稱該模塊是開放的。擴展一個模塊意味著增加該模塊的能力或擴展它的功能。有開放模塊是有好處的,因為這樣使擴展和修改系統(tǒng)成為可能。由于系統(tǒng)需求很少是穩(wěn)定不變的,容易擴展模塊的能力是降低系統(tǒng)維護費用的一個重要方面。開-閉原則說明,開發(fā)者應(yīng)當(dāng)力求使所設(shè)計的模塊既是開放的同時又是封閉的。如上所述,從既開放又封閉的模塊可以得到重要
5、的好處。然而初看起來,開-閉原則似乎有些似是而非,因為很難設(shè)想,一個模塊怎么可能既是開放的同時又是封閉的。如果“開放”的定義是指能夠改變一個模塊,那么一個模塊既是開的又是閉的是有矛盾的。然而“開放”的定義只是說一個模塊應(yīng)當(dāng)是可以擴展的。為了避免矛盾就必須找出種辦法,可以擴展該模塊而又不改變該模塊。解決這個問題的一般方案是區(qū)分一個模塊的接口和它的實現(xiàn)。如果一個模塊的這兩個方面可以分離,使客戶模塊僅依賴它的供應(yīng)者模塊的接口, 那么供應(yīng)者模塊實現(xiàn)的修改就不 會影響客戶模塊。面向?qū)ο蟪绦蛟O(shè)計語言提供了許多方法, 使一個類的接口可以與它的實現(xiàn) 區(qū)別開來,本節(jié)將對支持開-閉原則需要的相應(yīng)的機制,做一簡要的
6、描述和評價。14. 1. 1數(shù)據(jù)抽象使用數(shù)據(jù)抽象的意圖,是通過使實現(xiàn)細(xì)節(jié)對客戶代碼不可見的辦法,將數(shù)據(jù)類型或者類與它的實現(xiàn)相分離。這樣,可以設(shè)想數(shù)據(jù)抽象能夠構(gòu)造一個既是開放同時又是封閉的模塊。在面向?qū)ο蟪绦蛟O(shè)計語言中,數(shù)據(jù)抽象是通過指定類的每個特征的訪問級,例如“公有的”或“私有的”提供的。圖 14.2通過對供應(yīng)者類的特征定義典型的訪問級,表示了一般的客 戶-供應(yīng)者關(guān)系。圖14.2使用數(shù)據(jù)抽象的客戶-供應(yīng)者關(guān)系訪問級在UML中也稱為 可見性,它指明了客戶可以看到一個類的哪些特征。圖14.2中供應(yīng)者類中的操作聲明為公有的因此客戶可以看到,而屬性是私有的因而是不可見的。從客戶的視角看,類的接口是僅
7、有的可見的特征。如果可見的接口保持不變,不可見的特征可以改變、去掉或增加,都不會對客戶產(chǎn)生影響。例如,在Java中,圖14.2中的供應(yīng)者類可以如下實現(xiàn)。i c czlaB-a Suppl private lilt attr ifcute ;public void 口peration (J -| / / 1 irp le menizsi t Lon o f crps irat ioniI 這個類中公有方法的實現(xiàn)可以改變而不會對客戶類有任何影響,類似地,支持該類的 方法實現(xiàn)所需要的私有域也可以增加或去掉。為了避免影響客戶,必須保持不變的只是由該類公有方法的名稱和特征標(biāo)記(sig nature)組成
8、的可見接口。然而實現(xiàn)開-閉原則的這種方式有許多局限。從根本上講,由于系統(tǒng)的修改要求改變客 戶類的代碼,客戶模塊在技術(shù)上是不可能關(guān)閉的。如果更實質(zhì)的目標(biāo)可以達(dá)到,這種改變可以作為字面上的違反來看待,而不是違背了開閉原則的精神實質(zhì),但是數(shù)據(jù)抽象方式還存在另外的更本質(zhì)的問題。首先,雖然在Java中私有域可以被增加到類中而不會影響客戶,但并不是所有程序設(shè) 計環(huán)境都是如此。例如,在C+中類的定義典型地是由頭文件和實現(xiàn)文件分擔(dān)的,頭文件實際上是被合并到客戶模塊中去的。這樣,對頭文件的任何改變,例如,增加一個新的域,就 需要重新編譯客戶模塊,即使這種改變對客戶模塊是不可見的。如果開-閉原則的實現(xiàn)可以做到獨立
9、于語言,是更可取的。其次,在數(shù)據(jù)抽象方式中,客戶模塊所需要的接口仍處在隱含的狀態(tài)??蛻艨赡苁褂霉?yīng)者提供給他的可見到的所有特征,但也可能并不需要。實際上一個模塊的不同客戶或許使用的只是一個模塊可見接口的不同子集。這就很難準(zhǔn)確地知道,對一個模塊的哪些改變會影響給定的客戶。通過將客戶模塊所需要的接口用文檔明確地加以描述,將會改進(jìn)文檔的編制和可維護性。14. 1. 2抽象接口類實現(xiàn)開-閉原則的另一種方式是使用抽象接口類。圖14.3的類圖表示了用這種技術(shù)設(shè)計的一般結(jié)構(gòu)。圖14.3使用抽象接口類的客戶-供應(yīng)者關(guān)系這里抽象供應(yīng)者類定義了供應(yīng)者類的接口。由于抽象供應(yīng)者是一個抽象類,所以它沒 有定義屬性。供應(yīng)
10、者類的實現(xiàn)則交給定義了必需的屬性并實現(xiàn)了抽象類所聲明的功能的一個 具體子類。該客戶類聲明了它對接口類的依賴,接口類所包含的實現(xiàn)細(xì)節(jié),如果有的話,也只是已知不會再修改的細(xì)節(jié)。在Java中,抽象供應(yīng)者類可以聲明為由具體供應(yīng)者類擴展的抽象類。具體類定義被選擇的實現(xiàn)所必需的屬性并定義所有必需的成員函數(shù)。puplic apetractAfetractSupplierIpublic abstract void operationf/?卜publ-icCo-acreLaSuppLiex cxL-enduppl i-ci:Iprivate int attribute j-IjllLILc void opera
11、tion() H I mp 1 e irlEtl tat loti O f ope rat Lon*從開-閉原則的觀點看,這個實現(xiàn)最重要的是對三個類之間依賴關(guān)系的影響。正如第11章所闡述的,泛化關(guān)系引起子類和超類之間的依賴性。因而圖14.3中三個類之間的使用依賴可以用圖14.4予以表示。Ii ©use葉iii圖14.4帶有抽象接口類的依賴圖14.4清楚地表明,客戶類并不依賴于具體供應(yīng)者類,結(jié)果是具體類的任何方面都可以改變而不會對客戶模塊產(chǎn)生影響。應(yīng)當(dāng)記住,我們這里講的是編譯時的依賴。典型地,在運行時客戶可以保持或操縱對 一個具體供應(yīng)者類的實例的引用,但這個引用是保存在一個被聲明為對抽
12、象供應(yīng)者的引用的域中。由于引用的多態(tài)特性,這個域并不限于保持對該抽象供應(yīng)者類的實例的引用。因此,這個抽象供應(yīng)者類,至少在它可以被進(jìn)一步增加的子類所擴展的意義上,是開放模塊的一個例子。新增加的子類可以提供該接口的另一種可供選擇的實現(xiàn),或者增加系統(tǒng)中以前沒有的特征。例如,在預(yù)約系統(tǒng)中的預(yù)約類展示了可擴展性的這個特征。另外的子類可以增加進(jìn)來使系統(tǒng)能夠處理新種類的預(yù)約,但這并不會影響任何客戶模塊, 譬如餐館或預(yù)約系統(tǒng)。抽象供應(yīng)者類也是關(guān)閉的嗎?肯定地說,它比圖 14.2中的供應(yīng)者類更關(guān)閉,在圖14.2中,對供應(yīng)者對象實現(xiàn)的典型改變是對它的具體子類做出的而不是對抽象供應(yīng)者本身。然而,隨著系統(tǒng)的演進(jìn),可能
13、需要改變接口,這將要求改變抽象類。你可能會問,是否有方法關(guān)閉一個模塊,以抵御這種改變。這個題目將在14.3節(jié)考慮。使用抽象接口類,如圖 14.3所闡明的,是面向?qū)ο笤O(shè)計的基本技術(shù),在本章后面介紹 的模式中將會反復(fù)使用這些例子。14.2無具體超類14.1節(jié)討論了抽象接口類在提供滿足開-閉原則的軟件模塊中的效用。 相關(guān)的原則表明,在泛化關(guān)系中所有子類應(yīng)當(dāng)是具體的,或者反過來說,在泛化層次中所有非葉類應(yīng)當(dāng)是抽象的。這個原則可以概括為一句話,“無具體超類”。這個原則的基本原理可能最適合通過例子來理解。假定銀行正在實現(xiàn)若干個類作為不 同類型賬戶的模型,如在 8.5節(jié)所考慮的例子,這個模型的最初版本只定義
14、了一個表示活期 賬戶的類,具有通常的取款和存款操作。后來該銀行又增加了儲蓄賬戶并增加了根據(jù)賬戶余額支付利息的功能。由于這些新賬這樣戶可以共享活期賬戶的大部分功能,決定將儲蓄賬戶類作為活期賬戶類的特化來定義,就可以繼承共享的功能。圖14.5表示了這種情況。圖14.5具體超類的例子這個設(shè)計明顯地違背了“無具體超類”原則,因為圖 14.5中的活期賬戶類既是具體類 又是超類。隨著設(shè)計的演進(jìn),違反這個原則可能會導(dǎo)致嚴(yán)重的問題,我們將通過接著開發(fā)上述例子說明。例如,假設(shè)最初定義的取款操作不允許賬戶透支。然而這種限制使很行活期賬戶得不到客戶的歡迎,銀行決定允許活期賬戶透支而不允許儲蓄賬戶透支??墒且薷膱D1
15、4.5的設(shè)計以提供這種功能并不容易。例如,簡單地修改在活期賬戶中定義的取款操作以允許透支,并不是正確的解決辦法。由于這個操作已被儲蓄賬戶類繼承,這將會導(dǎo)致也允許儲蓄賬戶中的提款操作透支的不正確結(jié)果。為了提供原有的功能,在儲蓄賬戶中覆蓋這個取款操作是可以的,但這是一個很勉強的不自然的解決辦法。更嚴(yán)重的后果是,這兩個類實際執(zhí)行的取款操作的代碼的重復(fù)。在這個例子中這種重復(fù)問題不大,但一般地,代碼的重復(fù)是設(shè)計錯誤的一個主要征兆。另一種可能是在活期賬戶中定義的取款操作有辦法檢查取款操作是由運行時的哪種類型賬戶做出的,然后選擇相應(yīng)的動作路徑。下面一段代碼扼要地說明了這個操作的實現(xiàn)。pob-1 ic 口Iv
16、czd xj. Liidf aw (double amt if (this Lnstanceof SaviugaAc count) / Cheek i± bee 匚over Jr awnbalaLU匚e -=. a.m.b $可是這種風(fēng)格的程序設(shè)計也有嚴(yán)重的缺點,它包括了超類 Current Account'對它的子類顯式地引用。這意味著當(dāng)增加新的子類時,活期賬戶類中的代碼一般地也必須改變,使活期賬戶類在 Meyer意義下不可能關(guān)閉。由于這個原因以及其他原因,在一個系統(tǒng)的設(shè)計中 求助于這種風(fēng)格的程序設(shè)計也有明顯的缺點。如果一個功能只被定義在活期賬戶中,也會產(chǎn)生類似的問題。例如
17、,假定該銀行想對活期賬戶增加兌付現(xiàn)金支票功能,而這種功能對儲蓄賬戶不能使用。這個功能最明顯的實現(xiàn),如下所示,是檢查現(xiàn)金支票(cashCheque)的變元在運行時的類型。void( CurreTitAccmjnt a ) ifSavinrjsflir:civunt) return ;f/ URHll cheque |在這些情況中問題的發(fā)生是由于圖 14.5中的活期賬戶類扮演了兩種角色。作為超類,它定義了所有賬戶對象必須實現(xiàn)的接口,而它又提供了該接口的缺省實現(xiàn)。在我們討論過的這些例子中,這些角色是沖突的。 特別是,當(dāng)特定的功能必須以某種方式與具體超類的實例相關(guān)時,這種沖突就會發(fā)生。對這類問題的一般
18、解決辦法是遵循所有超類都應(yīng)當(dāng)是抽象的規(guī)則。這意味著,在圖14.5中所表示的設(shè)計應(yīng)當(dāng)用圖14.6所示設(shè)計取代。圖14.6應(yīng)用“無具體超類”規(guī)則這樣一來,在賬戶Acco unt'超類中的取款操作可以執(zhí)行基本的取款,而可以把檢查 儲蓄賬戶不能透支的代碼放在覆蓋它的函數(shù)中。為了使用公用功能,如果需要,子類也可以調(diào)用在超類中的操作實現(xiàn)。此外,由于儲蓄賬戶類( Savings Accou nt)不再是活期賬戶類的 子類,也不再需要像上面那樣將活期賬戶的實例傳送給cashCheque'函數(shù),也沒有必要檢查該函數(shù)的變元在運行時的類型。14.3接口層次的解耦假定已經(jīng)按照圖14.3所示的設(shè)計思路實
19、現(xiàn)了抽象接口類。后來又需要修改,增加一個 功能到抽象供應(yīng)者類提供的接口,以滿足新的客戶類的需要。圖14.3給出的設(shè)計看來并沒有提供直接把這個功能增加到抽象接口類的辦法。所以這 種方式仍然有需要認(rèn)真對待的問題。首先,目標(biāo)是關(guān)閉這個抽象供應(yīng)者類,因此不應(yīng)當(dāng)修改這個類。其次,這種改變可能導(dǎo)致對現(xiàn)有客戶的進(jìn)一步改變,至少會迫使對它們進(jìn)行再編譯。或者由該抽象供應(yīng)者提供一第三,新的功能還必須在所有已有的具體供應(yīng)者類中予以實現(xiàn), 個缺省實現(xiàn)。這些問題可以通過定義一個作為接口的抽象供應(yīng)者,而不是作為供應(yīng)者類層次中的基類定義的抽象供應(yīng)者來避免。 如果這樣做,具體供應(yīng)者類不再是抽象供應(yīng)者的子類, 而代替 的是提供
20、了它所定義的該接口的實現(xiàn)。在圖 14.7所示的這個新設(shè)計是圖 14.3所示的設(shè)計的 另一種選擇。圖14.7使用接口這樣就有可能做到不修改接口本身來處理擴展抽象供應(yīng)者接口的要求,以應(yīng)對新客戶 的需要。替代的是,可以定義一個新接口,它是這個抽象供應(yīng)者接口的特化。新客戶使用這 個新接口,而原有的客戶繼續(xù)使用原來未改變的接口。這個新接口本身可以由新具體供應(yīng)者類實現(xiàn)。原有的抽象供應(yīng)者接口并未改變,所以它是一個名副其實的關(guān)閉模塊。圖14.8表示了這個結(jié)果情況。圖14.8用特化擴展接口注意,圖14.8并沒有表示具體供應(yīng)者和新的具體供應(yīng)者類之間的泛化關(guān)系。實現(xiàn)新具體供應(yīng)者的一種方法是繼承由具體供應(yīng)者實現(xiàn)的抽象
21、供應(yīng)者的功能(冒著運行違反無具體超類規(guī)則的風(fēng)險),而另外的實現(xiàn)也是可能的。例如,可以在新具體供應(yīng)者類中包含一個對具 體供應(yīng)者實例的引用,并委派它做相應(yīng)的功能調(diào)用,或者對每件事都簡單地從頭重新實現(xiàn)。因為客戶模塊現(xiàn)在只是間接地與具體供應(yīng)者相關(guān),不同供應(yīng)者也就不需要為了提供多態(tài)客戶接口而通過泛化相關(guān):這是由接口之間的泛化提供的。14.4 LISKOV替換原則如果使用如14.1節(jié)描述的抽象接口類,那么對于根據(jù)抽象接口類定義的接口編程的客戶模塊,會廣泛使用多態(tài)。例如,在圖14.3中,該客戶只知道抽象供應(yīng)者。這意味著,該客戶如下所示,將通過引用抽象供應(yīng)者( Abstract Supplier)類型調(diào)用供應(yīng)
22、者(Supplier )的 操作。AtstracrtEiapp I ier Fuppl ier ;supplier_ operation) 當(dāng)這段代碼運行時,此Supplier '變量并沒有包含對Abstract Supplier '的實例的引 用:因為這是一個抽象類,不可能有實例。代替的是它將保持一個對Concrete Supplier'類的實例的引用或者該抽象供應(yīng)者類的某個另外子類的實例的引用。如果這種多態(tài)性在程序中不會引起問題,那么,該程序語言必須保證下述為真:如果 一個客戶期望保持對類T的一個對象的引用,那么當(dāng)用T的一個特化類 S的一個對象引用替代時,同樣會滿意
23、地工作。Liskov替換原則 是1987年計算機科學(xué)家 Bavbara Liskov給出一個經(jīng)典陳述后命名的。 這個原則給出了一個類型是另一個類型的子類型意味著什么的定義,在效果上提供了所需要的保證。在這個語境中該原則可以敘述如下。如果下述為真,那么類 S就正確地定義為類 T的特化:對類 S的每個對象s,存在著 類T的一個對象t,使得根據(jù)類T定義的任何程序 P的行為,如果用s替換t,其行為不會 改變。非形式地說,這就意味著,子類的實例可以替換超類的實例而不會對客戶類或模塊產(chǎn) 生任何影響。這個原則的一個推論是,超類之間的關(guān)聯(lián)也可以被它們的子類所繼承,因為對在一個鏈接的另一端的一個對象來說,它是否
24、被鏈接到一個用子類對象代替的超類對象并沒有什么不同。雖然這個定義是用類型和子類型的語言表述的,但這個Liskov替換原則還是有效地定義了面向?qū)ο蟪绦蛟O(shè)計語言中使用的泛化概念的意義。在UML中,例如在類之間、用例之間和參與者之間所定義的各種形式的泛化,都共享這個性質(zhì),即在這樣的關(guān)系中總可以用特化的實體的實例替換更一般的實體的實例。準(zhǔn)確地說,這具體意味著什么將依賴于所考慮的實體的類型。例如,在參與者情況下 意味著,如圖4.5所示,特化參與者,作為更一般的參與者,可以參與更一般的參與者與之 交互的所有用例的交互。因此,類之間的泛化只有在超類的當(dāng)前值可以由子類的當(dāng)前值自由地替換的情況下,才能正確地使用
25、,客戶模塊不可能察覺這種替換。在任何環(huán)境,如果把子類對象用在超類對象的位置,一個程序會有不同的行為,那么就是不正確地使用了泛化。程序設(shè)計語言是在語法層上檢查可替換性的,它將檢查用這種方式定義的子類在運行時不會發(fā)生某些種類的錯誤,例如,向一個對象發(fā)送一個它不理解的消息。但是程序員在用這種方式實現(xiàn)派生類中的操作時,總是可能由于提供了使子類實例以完全不同于超類實例的方式行為的實現(xiàn),而不自覺地?fù)p害了可替換性。在研究文獻(xiàn)中已經(jīng)做了許多努力,試圖描述更強的替換性概念,以保證子類提供的行為能以某種方式與超類中定義的行為兼容。但是這種要求必須謹(jǐn)慎地表述:要求子類提供與超類一樣的行為是不合適的,因為在許多情況下
26、,定義一個子類的全部特征就是定義這些對象的某些類特有的行為。處理這個問題的一種方式是對泛化關(guān)系中的這些類所定義的約束之 間的關(guān)系做出明確說明。14.5交互決定結(jié)構(gòu)這個例子的基礎(chǔ)是 Robert Martin ( 1998)的一篇論文。它闡述了在預(yù)約系統(tǒng)的開發(fā)中使用的啟發(fā)方法,即根據(jù)對象之間的交互用作確定設(shè)計中的對象所屬的類之間的結(jié)構(gòu)關(guān)系。假定我們要建立一個簡單的移動電話模型,電話的接口由一組撥號按鈕,一個開始呼 叫的“發(fā)送”按鈕和一個結(jié)束呼叫的“清除”按鈕組成。一個稱為撥號器的部件與各種按鈕 交互并跟蹤當(dāng)前呼叫狀態(tài)和截止當(dāng)前已撥過的數(shù)字。蜂窩無線電處理到蜂窩網(wǎng)的連接。此外電話機還有一個微受話器
27、,一個送話器和在上面可以顯示所撥號碼的顯示屏。圖14.9給出了根據(jù)這個描述做出的一個沒有經(jīng)驗的移動電話設(shè)計。這個圖給出了一個直觀上清晰的移動電話的物理結(jié)構(gòu)模型。每個部件都用類表示, 聚合用來說明各種部件都是由移動電話類表示的這個組裝體的一部分。圖14.9移動電話的物理模型這個設(shè)計是否適當(dāng),可以通過察看是否支持實現(xiàn)要求該電話的行為來測試。例如,考慮下述腳本,它描述了一個用戶用此電話呼叫時可能發(fā)生的基本事件流。1. 用戶輸入呼叫的電話號碼。2. 每個按下數(shù)字被添加到顯示器上。3. 用戶按下“發(fā)送”鍵。4. 無線收發(fā)器建立到網(wǎng)絡(luò)的連接并發(fā)出呼叫。5. 顯示器顯示該電話現(xiàn)在忙。6. 這次呼叫結(jié)束,用戶
28、按下“清除”鍵。7. 顯示器被清除。圖14.10表示了這個腳本從開始到建立連接為止的一個實現(xiàn)。當(dāng)用戶按下一個鍵時,一個給出了所按下的那個數(shù)字的信息的消息被發(fā)送到撥號器;然后,這個數(shù)字被添加到顯示器上。當(dāng)用戶按下發(fā)送按鈕,撥號器把全部號碼發(fā)送到無線收發(fā)器,由它呼叫,一旦被連接即更新顯示器。圖14.10用移動電話進(jìn)行呼叫從這些圖可以直接看出,圖14.9類圖中的關(guān)聯(lián)并不支持圖 14.10表示的交互路徑中被發(fā) 送的消息。為了得到一致的設(shè)計, 或者改變移動電話的靜態(tài)模型, 或者使這個腳本的實現(xiàn)與 圖14.9中的模型一致。在此例中可能更可取的是采用由圖14.10所表示的交互圖所蘊含的結(jié)構(gòu)。雖然上述腳本的實
29、現(xiàn)也可以建立在圖14.9所示的結(jié)構(gòu)之上,但若強使這些對象之間的交互都通過中央移動電話對象安排路徑,將導(dǎo)致更復(fù)雜的交互,迫使電話對象跟蹤走過交互的每個細(xì)節(jié)。更好的方式是,所采用的靜態(tài)結(jié)構(gòu)是根據(jù)為支持系統(tǒng)交互所需要的結(jié)構(gòu)而建立的靜態(tài)結(jié) 構(gòu)模型。圖14.11表示的是按照這種方式得到的移動電話模型。圖 14.11 更好的移動電話模型這個模型和圖 14.9 所示的模型之間最顯著的不同也許是沒有任何必要再設(shè)置一個對象 表示移動電話本身, 因此, 移動電話類和鏈接到電話的不同部件之間的聚合關(guān)系,在這個模型中也就不再出現(xiàn)。作為結(jié)果,也許可以說這個模型沒有像原來那個模型很好地表示現(xiàn)實世界。 然而認(rèn)為“現(xiàn) 實世界
30、” 只可能用一種方法正確地建模的觀念是過于簡單化了。 這個例子的教益是, 我們建 模的對象之間的物理關(guān)系, 可能沒有提供合適的基礎(chǔ)支持系統(tǒng)功能所需要的交互。 在這種情 況,采用能更好地支持這種必須的交互的靜態(tài)模型,通常是更可取的。14.6 設(shè)計模式在任何設(shè)計活動中都存在著某些重復(fù)遇到的典型問題。 不同設(shè)計師對這些問題設(shè)計出不 同的解決方案, 隨著設(shè)計經(jīng)驗在實踐者之間日益廣泛地流傳, 描述了這些共同問題和解決這 些問題的方案的大量知識也在逐漸形成。 但是這些方案往往不是完全顯然的, 而且涉及到一 些不是廣為人知的職業(yè)竅門' 。使這些知識更清晰明確并可以公開地得到和利用, 顯然是有好處的。
31、 或許最重要的是這 將使沒有經(jīng)驗的設(shè)計師可以訪問這些可以直接應(yīng)用的技術(shù)庫, 這樣會使他們擴展相關(guān)設(shè)計技 術(shù)中的專門知識的過程更加容易。 就面向?qū)ο笤O(shè)計來說, 設(shè)計模式 已經(jīng)推薦為編篡內(nèi)行設(shè)計 師處理和解決在設(shè)計中通常遇到的特定問題的方法的一種方式。1995 年出版的由 Erich Gamma, Richard Helm, Ralph Johnson 和 John vissides 所著的 設(shè)計模式 一書,是論及這個主題的典范。這本書以“四人組”的稱號廣為人知,在本章的其 余部分也會這樣引用。設(shè)計師面對的問題來自不同層次。 在最低層,涉及的是單個類的接口或?qū)崿F(xiàn)的細(xì)節(jié)問題。 在最高層, 涉及的是系
32、統(tǒng)的整體構(gòu)架的創(chuàng)建問題。 設(shè)計模式關(guān)注的是中間層, 在這一層必須 保證局部化的特定的設(shè)計性質(zhì)。設(shè)計模式關(guān)注的典型問題可以包括下列問題。設(shè)計如何保證在系統(tǒng)內(nèi)部的一個類始終 只有一個實例被創(chuàng)建?如何建立像樹一樣的遞歸模型?如何動態(tài)地將追加的功能增加到一 個對象?對一個設(shè)計模式提出的問題的解決方案,典型地是作為面向?qū)ο笤O(shè)計的一個不完整部 分表達(dá)的, 它由一系列交互的類組成, 它們結(jié)合在一起對所提出的問題提供了預(yù)制的解決方 案。然而一個模式通常可以被應(yīng)用到許多情況,因此,這個解決方案描述的是一個模板,或 構(gòu)造型,當(dāng)需要時可以加以改寫或者重用的設(shè)計。14 6 1 模式的定義模式常常定義為“對一個語境中的
33、問題的解決方案” 。對面向?qū)ο笤O(shè)計中的問題的解決 方案,可以用類圖或交互圖這類規(guī)范化的表示法簡明地加以描述。 但至少由于兩個原因不應(yīng) 當(dāng)認(rèn)為規(guī)范化的表示法與模式是同一件事。首先,許多模式共享類似的規(guī)范結(jié)構(gòu), 而卻又是為在完全不同的情況下使用而設(shè)計的。 一個模式不同于另一個模式的不可缺的部分是理解它所關(guān)注的問題, 即使所建議的在兩種或 多種情況下的解決方案看起來可能在形式上是類似的。其次,大多數(shù)模式可以使用在細(xì)節(jié)上可能不同的類結(jié)構(gòu)或交互,以不同方法的變種表 示或?qū)崿F(xiàn)。這樣,在細(xì)節(jié)上完全不同的設(shè)計,可能事實上卻是同一模式的應(yīng)用。四人組的書給出了定義一個模式必不可少的基本要素如下。1名稱 。模式有一
34、個助記的名稱,幫助設(shè)計師記住它。命名了的模式的集合也提供了 一種行話,使設(shè)計師有可能在模式級上討論設(shè)計。2問題 。它定義了該模式可以應(yīng)用的情況。3 解決方案 。它描述了處理該問題的設(shè)計元素。這個方案常常是用適當(dāng)?shù)囊?guī)范表示法 表達(dá)的,但如上面所強調(diào)的,一個模式圍繞著共同思路常常有一系列變種。4效果 。這是應(yīng)用該模式的結(jié)果和權(quán)衡。如果在一個特定情況下,已經(jīng)做出了是否應(yīng) 該使用該模式的非正式?jīng)Q定,那么,知道使用一個模式的效果是絕對必要的。因此,設(shè)計模式的概念是不太規(guī)范的。這種不規(guī)范性使得當(dāng)遇到兩個模式是一樣的, 或者一個設(shè)計是否使用了一個給定的模式這類問題時, 很難做出明確的回答。 模式界許多人 在
35、為這種不規(guī)范性辯護,理由是模式捕捉到的是“設(shè)計見解” ,它抵制的就是不受規(guī)范性的 損害,如果定義的太狹隘,就會損害對可應(yīng)用性的理解范圍。另一方面由于沒有一個清晰的概念說明一個模式的定義實際上由哪些部分組成,要想 分辨兩位作者是否描述了相同的模式, 一個模式的變種, 或者兩個不同的模式, 還是找出一 個可用于給定情況的模式, 都是困難的。 隨著出版的模式文獻(xiàn)的擴展, 這些問題也變得更令 人擔(dān)心。14 6 2 模式和框架模式和框架是記錄或重用某些設(shè)計元素的兩種方式,有時常會混淆。但它們在所提供的東西和如何使用的意圖上有很大不同。首先,模式和框架在利用的 規(guī)模 上是不同的??蚣芏x了一個完整應(yīng)用或者
36、相關(guān)的一類應(yīng)用的構(gòu)架。 另一方面, 模式描述的是單個設(shè)計問題的解決方案, 它可以應(yīng)用到許多應(yīng)用 或框架中。其次,模式和框架有不同的 內(nèi)容 。一個模式是純粹的設(shè)計思路,它可以用不同語言以 不同方法改寫和實現(xiàn)。 框架典型地是設(shè)計和代碼的混合物, 它可以由應(yīng)用程序以各種方式擴 展。第三,作為第二點的推論,模式比框架更關(guān)注 可移植性 ??蚣苁且呀?jīng)實現(xiàn)了的,雖然 并沒有必要組成一個完整的應(yīng)用, 因此常常受限于一個單一的實現(xiàn)環(huán)境。 模式是獨立于語言 的,可以在廣泛的不同情況中得到應(yīng)用。第三個概念,也帶有重用的隱含意義的,是“構(gòu)件”的概念。構(gòu)件通??紤]的是使代碼 重用并至多允許在有限程度上按規(guī)格定制。 在文
37、獻(xiàn)中已經(jīng)有了 “框架等于構(gòu)件加模式” 這種 說法,但這種說法貶低了框架在定義完整系統(tǒng)構(gòu)架中的作用。14.7 遞歸結(jié)構(gòu)在本書前幾章的例子中,我們曾考慮過一個對象連接到相關(guān)類的若干個對象的情況, 包括它自身所屬的類的對象, 并且需要對這些對象以完全類似的方式進(jìn)行處理。在第 2 章庫存控制系統(tǒng)中的組件就是一個例子。在這個例子中,一個組件可以包含未明確指明數(shù)目的其他對象, 其中某些是簡單零件, 另一些本身又是組件。這些子組件除可以包括簡單零件外,還可以依次地包括另一些組件, 這個組件的嵌套可以一直繼續(xù)下去, 直到所需要的程度。 這里在許多方面, 零件和組件都是 以完全相同的方式處理的,并且零件類和組件
38、類共享相同的接口。這種情況可以通過引入一個新類來定義共享的共同特征來建模。在第 2 章這個類稱為 Component ',圖 14.12 表示的是描述了這個構(gòu)件結(jié)構(gòu)的類圖的有關(guān)部分。CompojiPJ:!toompononts丄LPartAsseTibyoosHCDSlt j IIL%uk i Idtii c n conpofnts l,'letum 的tiy.gdCost)號 1 站 += c jm/U j圖14.12 零件和組件'構(gòu)件是一個抽象類,它描述了零件和組件的共同特征。確定構(gòu)件成本的操作是抽 象操作,它必須在每個子類中覆蓋。對于零件,此操作將從與它相關(guān)聯(lián)的目
39、錄條目(catalogueEntry)對象(圖14.12中未表示)查找該零件的價格。對于組件,此操作將迭代通過包含在該組件中的所有構(gòu)件,調(diào)用每個構(gòu)件中的價格操作,返回所有返回值的和。這些特性表示在圖14.12對這些操作所附加的注解中。組件和它所包含的構(gòu)件之間的鏈接是通過從組件類到構(gòu)件超類的關(guān)聯(lián)建模的。這里說明了一個組件可以有0個或多個構(gòu)件,每個構(gòu)件可以是零件或組件。圖14.12中的關(guān)聯(lián)不同于第2章給出的關(guān)聯(lián)在于,這里的組件被定義為它的構(gòu)件的聚件(aggregate) o女口 6.9節(jié)所說明的,這就保證了組件有著嚴(yán)格的樹結(jié)構(gòu),所以組件決不可能包含它自身,無論是直接地包含或間接地包含。零件和組件有
40、兩個重要的語義性質(zhì)使得在圖14.12中采用這種泛化是恰當(dāng)?shù)摹J紫?,它們共享共同的接口,共同的超類定義了這個接口。其次,一個組件的總的結(jié)構(gòu)是一個遞歸定義的樹結(jié)構(gòu),在此結(jié)構(gòu)中,組件的內(nèi)容可能是零件或其他組件。14. 7. 1組合模式圖14.12舉例說明的結(jié)構(gòu)是經(jīng)常見到的,四人組的書中是在命名為組合(composite)的模式中記錄了它的本質(zhì)屬性。組合模式的意圖是“將對象組合成樹形結(jié)構(gòu)以表示部分整體層次結(jié)構(gòu)。組合使得用戶可以一致地處理單個對象和組合對象”。一個模式表達(dá)的解決方案典型地是用類圖記錄在文檔中的。此類圖表示了一些類可以怎樣相互交互,以提供所需要的功能。在組合情況中,組合模式的描述非常類似于
41、圖14.12所示的它的應(yīng)用,但對所參與的類給予了更一般的名稱。圖14.13中所表示的是在四人組書中所定義的組合模式的簡化版本。圖12.13組合模式的結(jié)構(gòu)這個圖表示了一個遞歸結(jié)構(gòu)的構(gòu)件可以是一個葉,它將不再有它自己的子構(gòu)件,或者 是一個組合,它可以有任意多個孩子構(gòu)件。在庫存控制例子中,零件類對應(yīng)于葉類,而組件 類對應(yīng)于這里稱為組合的類。這里的構(gòu)件類定義了一個一致的接口,客戶通過此接口可以訪問和操縱組合結(jié)構(gòu)。在圖14.13中,這是用抽象操作表示的。這個接口必須在每個子類中明確地定義,在組合類中 該操作的實現(xiàn)通常將調(diào)用它的每個孩子的操作。這個操作用附加到組合類中該操作的注解指明。圖14.13的類圖也
42、定義了一個類屬結(jié)構(gòu),使類-樹結(jié)構(gòu)可以由程序來建造。希望使用這個模式的設(shè)計師可以利用這個圖,標(biāo)明應(yīng)用中對應(yīng)于葉和組合的類,并創(chuàng)建一個它們的共同超類。那么,該操作的定義就提供了一個可訪問該樹中每個節(jié)點的可重用的實現(xiàn)。14. 7. 2 UML中的模式設(shè)計模式可以設(shè)想為是一個可以在許多不同情況下被重用的一般目的的設(shè)計方案。圖14.13中的類并不是應(yīng)用系統(tǒng)中的類,而是更像一個占位符,在該模式的不同應(yīng)用中,它將 等同于不同的類。為了反映這個意思,在UML中這些模式是作為 參數(shù)化協(xié)作表示的。該模式將作為單- 的單元,協(xié)作,來對待,模式中的類的名稱定義為參數(shù),當(dāng)應(yīng)用該模式時,可以用實際類名 取代。圖14.14
43、表示了用這種形式表示的組合模式。圖12.14作為參數(shù)化協(xié)作的組合模式用這種方式表示模式的一個優(yōu)點是,一個模式的應(yīng)用可以記實性地記錄在一個類圖中。圖14.15表示的是庫存控制程序和組合模式的抽象表示合在一起的一個簡化的類圖。組合模式到類的連接是用模式中類的名稱標(biāo)明的,這就如實地記錄了設(shè)計中的哪些類對應(yīng)于該模式的實例化中的哪些特定角色。ConpMls !圖14.15模式應(yīng)用的文檔化14.8狀態(tài)和策略模式13.7節(jié)中描述的狀態(tài)圖的另一種實現(xiàn)可以建立在稱為“狀態(tài)(State)”的設(shè)計模式的應(yīng)用的基礎(chǔ)上,這是四人組書中的一個模式。狀態(tài)模式的意圖是 “允許一個對象在其內(nèi)部狀態(tài)改變時改變其行為。這個對象看起
44、來好像是改變了它的類。”圖14.16給出的是概括了狀態(tài)模式的結(jié)構(gòu)的類圖。這里Con text類表示的實體展示了依賴于狀態(tài)的行為。Con text對象的不同狀態(tài)是通過狀態(tài)層次中的類表示的;每個Con text對象每次只連接到這個狀態(tài)層次中的一個狀態(tài),該狀態(tài)表示Con text的當(dāng)前狀態(tài)。圖14.16狀態(tài)模式(請編輯注意,此圖取自光盤,有錯誤,應(yīng)以原書為準(zhǔn),見317頁)當(dāng)一個Con text對象接受一個請求時,它并不試圖自己來處理這個請求,而是委托此狀態(tài)類中的一個操作去處理這個請求。由于在這個狀態(tài)類的子類中提供了該操作的不同實現(xiàn), 這個Con text對象看起來好像是提供了動態(tài)變化的行為。圖14.
45、17表示的是與狀態(tài)模式有關(guān)的稱為“策略(Strategy)”的模式。策略模式通過用一個類的不同實例可以支持同一個操作的不同實現(xiàn)或者在運行時改變實現(xiàn)的辦法,允許選擇一個算法的不同實現(xiàn)。這里Con text對象的接口定義了一個操作,但Co ntext類并不實現(xiàn)它。這個實現(xiàn)是由與該對象相連接的策略類的一個對象提供該實現(xiàn),即把對這個操作的調(diào)用委托給了這個策略類的對象。但是盡管有這些不同,除了類和所涉及的操作的命名之外,策略模式的結(jié)構(gòu)與狀態(tài)模式幾乎沒有什么區(qū)別。然而狀態(tài)模式和策略模式仍然被認(rèn)為是不同的模式,因為它們針對的是不同的問題。 這個例子說明了與其說模式給出了結(jié)構(gòu)倒不如說圖給出了模式的結(jié)構(gòu)。但是,
46、在另一方面, 這也使人們對如何區(qū)別一個模式不同于另外一個模式提出了懷疑。例如,也許一個對象的不同狀態(tài)可以設(shè)想為對實現(xiàn)該對象的操作的選擇策略。還會遇到更復(fù)雜的情況,下一節(jié)將考慮一族模式,它們本質(zhì)上是同一觀念的實現(xiàn),不過在結(jié)構(gòu)上是不同的。圖14.17策略模式14.9 MVC,文檔/視圖和觀察者第5章描述的模型-視圖-控制者(MVC )架構(gòu)的基本思想是將所使用的數(shù)據(jù)與顯示和操 縱數(shù)據(jù)的用戶接口相分離。這個思想最初是在Smalltalk程序設(shè)計語言的環(huán)境中提出的。本節(jié)將簡短描述 MVC的結(jié)構(gòu)和樣式,其關(guān)鍵思想已在微軟的文檔/視圖架構(gòu)和觀察者模式中使用。模型-視圖-控制者圖14.8表示的是使用 MVC構(gòu)
47、架表示的一個應(yīng)用的通用結(jié)構(gòu)。這里有一個 模型(model)視圖(view)對象,每個視對象,它存儲和維護所關(guān)注的數(shù)據(jù)。與這個模型相連接的是若干圖對象負(fù)責(zé)以一種特定方式顯示這些數(shù)據(jù)。例如,如果模型包含某些統(tǒng)計數(shù)據(jù), 不同視圖可以用棒圖或餅圖顯示這些數(shù)據(jù)。與這個模型相連接的還有若干控制者(controller)對象,控制者對象負(fù)責(zé)檢出用戶的輸入并把這些輸入轉(zhuǎn)遞給其它對象。這個構(gòu)架的三種構(gòu)件通過消息傳遞進(jìn)行通信。圖14.8表示了典型交互的細(xì)節(jié)。憑籍這些交互,該用戶的某個動作將引起模型和與模型相連接的視圖的更新。圖14.18 MVC中的標(biāo)準(zhǔn)交互周期用戶的輸入是由特定的控制者檢出的,即在圖14.18中表
48、示的一組控制者中與用戶相連接的那個控制者檢出的。一般地,用戶的輸入可能改變模型的狀態(tài),該控制者向模型發(fā)送一個通知,將此改變告訴模型。 這個消息和交互的其他消息,典型地將把這些數(shù)據(jù)作為變量傳遞。為了清晰,圖14.18中沒有表示這些變量。當(dāng)模型接收到一個改變通知時,該模型將發(fā)送一個更新消息給與它相連接的所有控制 者和視圖,這些控制者和視圖統(tǒng)稱為模型的依賴者。當(dāng)一個依賴者接收到一個更新消息時, 它將詢問模型以取得模型狀態(tài)的最新信息,然后重新顯示所有受到影響的用戶接口部分。應(yīng)當(dāng)注意的是,一般說,控制者以及視圖都可能受到模型改變的影響。一個簡單例子是菜單選項。這種構(gòu)架的好處在于它清楚地分離了處理應(yīng)用數(shù)據(jù)
49、的代碼和應(yīng)用的用戶接口。這就使 得容易開發(fā)新類型的視圖而不會影響到模型。這種方式在Smalltalk環(huán)境是特別重要的,Smalltalk是大量使用圖形用戶接口的開拓者之一。14. 9. 2文檔/視圖架構(gòu)文檔/視圖架構(gòu)是由微軟作為構(gòu)造支持圖形用戶接口的應(yīng)用的標(biāo)準(zhǔn)方法提出的。它可以看作是MVC構(gòu)架的簡化,這里的文檔對應(yīng)于MVC中的模型,而視圖則是控制者和視圖兩種功能的組合。圖14.19表示的是文檔類和視圖類的結(jié)構(gòu)關(guān)系,以及引起它們進(jìn)行交互的某些操作。(ara I v n it viei/Listv->0nl4>1a! 圖14.19 文檔/視圖構(gòu)架(簡化表示)圖14.20表示了文檔/視圖
50、構(gòu)架用于像鼠標(biāo)移動這種用戶操作發(fā)生的典型交互的細(xì)節(jié)。注意,為了強調(diào)參與的對象是兩個用戶定義類CMy Document '和 CMyView '的實例,這個協(xié)作圖是用實例而不是用文檔和視圖角色對象畫出的。圖14.20典型的文檔/視圖交互文檔/視圖交互的總的結(jié)構(gòu)類似于圖14.18的MVC構(gòu)架。檢出用戶動作的視圖發(fā)送通知消息“ UpdateAIIView ”到它的文檔。然后這個文檔又發(fā)送更新消息到所有鏈接到此文檔 的視圖,包括發(fā)送這個通知的視圖。這個更新消息進(jìn)而調(diào)用該視圖的OnDraw'方法,而在執(zhí)行這個方法的過程中,視圖在顯示與它相鏈接的文檔之前,典型地將檢索該文檔的當(dāng)前狀
51、態(tài)。14. 9. 3觀察者模式分離模型和視圖這個主題的另一個變體是四人組書中定義的觀察者模式。這個模式的意圖是“定義對象間的一種1對多的依賴,使得當(dāng)一個對象改變狀態(tài)時,所有依賴于它的對象將被通知并相應(yīng)地更新”。圖14.21給出了表示了這個模式中涉及到的類的類圖。這個圖表示了到目前為止熟悉的一個模型和若干視圖之間的關(guān)系,在這個模式中模型稱為目標(biāo)(Subject),視圖現(xiàn)在稱為觀察者( Observer)。圖14.21觀察者模式觀察者模式意味著它可以應(yīng)用于比響應(yīng)用戶的動作而去更新圖形顯示更廣泛的情況。因此,觀察者對象沒有必要負(fù)責(zé)測測用戶的輸入,使用觀察者所涉及的交互通常比用MVC或文檔/視圖簡單。
52、不過如圖14.22所表示的,觀察者模式的交互基本結(jié)構(gòu)與早期考慮過的是 一樣的。一位未具體指明的客戶發(fā)送一個通知消息給某目標(biāo),然后目標(biāo)又發(fā)送更新消息到所有它的觀察者;這些觀察者又反過來可以查問該目標(biāo)的狀態(tài)。圖14.22觀察者模式中的交互14.10訪問者模式對庫存控制程序的應(yīng)用假定需要增加一個功能到庫存控制程序,以打印出一個組件中的所有零件和子組件。這種報告常稱為“零件剖析(parts explosion)”。一個簡單方式是類似于已存在的"cost”函數(shù),可以增加一個“ explode(剖析)”函數(shù)到這個層次中。在零件類實現(xiàn)的這個函數(shù)實現(xiàn)將打 印出一個簡單零件的細(xì)節(jié),而在組件類中它將通過
53、該組件的所有構(gòu)件調(diào)用每個構(gòu)件中的“ explode ”函數(shù)。這種方式可能存在的問題如下:1. 為了增加“ explode",該層次中的每個類都必須修改。如果層次太多,可能是不可行的,或者要付出非常昂貴的代價。2. 在“ cost”和“ explode”函數(shù)中控制通過組件的構(gòu)件的迭代的代碼是重復(fù)的。而在一個地方實現(xiàn)迭代以避免這種冗余更可取。3. “ part”類是這個應(yīng)用模型的一部分,這個方案讓part類直接負(fù)責(zé)產(chǎn)生輸出。然而在上一節(jié)討論的模式的基本原則是用戶接口代碼應(yīng)當(dāng)獨立于模型以容易修改和擴展。這些問題可以用訪問者模式處理。訪問者模式的意圖是“根據(jù)操作作用的對象結(jié)構(gòu)元素,描述被執(zhí)行
54、操作;使得可以在不改變各元素的類的前提下定義作用于這些元素的新操 作?!币虼嗽L問者提供了一種方法,用這種方法實現(xiàn)的類在14.1節(jié)所討論的意義上,既是開放的同時又是封閉的。訪問者的工作原理是把表示數(shù)據(jù)的類,在此例中是零件層次,與可以應(yīng)用到該數(shù)據(jù)上的操作相分離。在新的“ visitors(訪問者)”層次中,操作是用類表示的。這兩個層次之間的 連接是通過在零件層次中定義一個單個操作“accepts a visitor"做出的。圖14.23表示的是,為了找出一個構(gòu)件價格,如何使用這種技術(shù)實現(xiàn)一個單個操作。lorn圖14.23使用Visitor找出構(gòu)件價格對于這個設(shè)計,程序員為了找出一個構(gòu)件的
55、價格可以不再一般地調(diào)用該構(gòu)件類中的操作。代之的是,必須如下所述創(chuàng)建一個價格Visitor對象,并把此對象傳送給該構(gòu)件。CDBtVisitor visitor 三 new Co&tViBitaxjtub cost = compain旦工it 匚匸匸口匸j ;當(dāng)一個構(gòu)件接收到一個訪問者時會發(fā)生什么,依賴于運行時該構(gòu)件的類型。如果該構(gòu)件是零件,確定該零件價格的任務(wù)就被委托給該訪問者對象中的函數(shù)。而為了使訪問者對象得到所需要的信息,也要把對這個零件的引用傳送給這個函數(shù)。如果該構(gòu)件是組件,則要執(zhí)行通過該組件的所有子組件迭代,并且每個子組件都要依次接收該訪問者。用這種方式,該訪問者對象被從一個構(gòu)件
56、傳送到一個構(gòu)件,直到在該組件中的每個零件都被訪問。確定該組件價格的實際工作是在該價格訪問者對象中進(jìn)行的。下面給出的這個類的定 義說明了這個總價格是如何隨著迭代的進(jìn)行而得出的。publ ic as Cosprivate ini. tota 1 】public void vis itPart p) total p.cos匚C);pixb lie void visit (Jls s<ainbLy a>/ nul 1 lrrplenient.3tiQn這段代碼說明,價格訪問者類包括確定一個構(gòu)件價格的所有代碼??墒沁@個遍歷零件層次的通用代碼是在該組件類的接收函數(shù)中出現(xiàn)的。為了確定組件的價格,這段代碼除了確保組件的所有構(gòu)件都被訪問之外,什么都沒有做,所以
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 飯店股份分配協(xié)議書
- 共建大數(shù)據(jù)學(xué)院協(xié)議書
- 金屬回收調(diào)價協(xié)議書
- 醉酒死亡補償協(xié)議書
- 銷戶車輛賣車協(xié)議書
- 解除制作合同協(xié)議書
- 尿痛護理措施
- 遣散員工補償協(xié)議書
- 酒店合作框架協(xié)議書
- 銀行終止扣款協(xié)議書
- 心理咨詢的面談技術(shù)
- DBJ∕T13-374-2021 福建省鋼筋桁架疊合樓板技術(shù)標(biāo)準(zhǔn)
- 事故池管理的有關(guān)規(guī)定
- (word完整版)污水處理廠安全評價報告
- DB50∕T 867.6-2019 安全生產(chǎn)技術(shù)規(guī)范 第6部分:黑色金屬冶煉企業(yè)
- 新產(chǎn)品開發(fā)流程課件
- 高中語文部編版選擇性必修下冊第四單元 單元學(xué)習(xí)導(dǎo)航 課件 (8張PPT)
- 化妝品原料-PPT課件
- 重慶市參加企業(yè)職工基本養(yǎng)老保險人員退休審批表
- 混凝土結(jié)構(gòu)課程設(shè)計244
- 跨國道防護棚方案
評論
0/150
提交評論