![軟件工程課件:設(shè)計(jì)和模式_第1頁(yè)](http://file4.renrendoc.com/view12/M04/2C/37/wKhkGWXqNHiARlhgAAFx7sh9sRA153.jpg)
![軟件工程課件:設(shè)計(jì)和模式_第2頁(yè)](http://file4.renrendoc.com/view12/M04/2C/37/wKhkGWXqNHiARlhgAAFx7sh9sRA1532.jpg)
![軟件工程課件:設(shè)計(jì)和模式_第3頁(yè)](http://file4.renrendoc.com/view12/M04/2C/37/wKhkGWXqNHiARlhgAAFx7sh9sRA1533.jpg)
![軟件工程課件:設(shè)計(jì)和模式_第4頁(yè)](http://file4.renrendoc.com/view12/M04/2C/37/wKhkGWXqNHiARlhgAAFx7sh9sRA1534.jpg)
![軟件工程課件:設(shè)計(jì)和模式_第5頁(yè)](http://file4.renrendoc.com/view12/M04/2C/37/wKhkGWXqNHiARlhgAAFx7sh9sRA1535.jpg)
版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡(jiǎn)介
設(shè)計(jì)和模式11.1設(shè)計(jì)在軟件生命周期中的作用11.2設(shè)計(jì)工作流11.3設(shè)計(jì)模式11.4規(guī)劃設(shè)計(jì)工作11.5設(shè)計(jì)包或子系統(tǒng)11.6設(shè)計(jì)工作流:考勤系統(tǒng)實(shí)例研究11.7HTMLProduction框架11.8TimeCardUI包11.9設(shè)計(jì)度量與用于設(shè)計(jì)的CASE工具11.10小結(jié)習(xí)題11
知識(shí)點(diǎn)
設(shè)計(jì)工作流,設(shè)計(jì)模式,規(guī)劃設(shè)計(jì)。
難點(diǎn)
如何將理論與實(shí)踐結(jié)合。
基于工作過程的教學(xué)任務(wù)
通過本章學(xué)習(xí),了解什么是設(shè)計(jì);搞清楚設(shè)計(jì)在軟件生命周期中的作用;了解設(shè)計(jì)模式的好處和作用;掌握設(shè)計(jì)工作流,進(jìn)行相關(guān)的構(gòu)架設(shè)計(jì),學(xué)習(xí)設(shè)計(jì)用例、類、子系統(tǒng)等;規(guī)劃設(shè)計(jì)工作,進(jìn)行面向?qū)ο笤O(shè)計(jì);通過考勤系統(tǒng)實(shí)例研究,學(xué)習(xí)設(shè)計(jì)工作流,理解設(shè)計(jì)過程。
在過去的幾十年間,人們提出了數(shù)以百計(jì)的設(shè)計(jì)技術(shù),其中一些是對(duì)已有技術(shù)的改進(jìn),另一些則與原有的完全不同,只有少部分的設(shè)計(jì)技術(shù)被成千上萬的軟件工程師所使用,而大部分僅僅是那些作者自己使用。一些設(shè)計(jì)策略,特別是那些由理論專家提出的,有著堅(jiān)實(shí)理論基礎(chǔ)的,或者更實(shí)用的,之所以提出來是因?yàn)槟切┳髡甙l(fā)現(xiàn)它們?cè)趯?shí)際工作中效果很好。大部分設(shè)計(jì)技術(shù)都是人工的,但是自動(dòng)化正漸漸成為設(shè)計(jì)的一個(gè)重要方面,特別是有助于文檔管理。
在如此多的設(shè)計(jì)技術(shù)中,一個(gè)確定的基本模式漸漸形成。一個(gè)軟件產(chǎn)品的兩個(gè)必需元素是操作和用于操作的數(shù)據(jù),所以,設(shè)計(jì)一個(gè)產(chǎn)品的兩種基本方式是面向操作的設(shè)計(jì)和面向數(shù)據(jù)的設(shè)計(jì)。在面向操作的設(shè)計(jì)中,強(qiáng)調(diào)的是操作,例如,數(shù)據(jù)流程分析,其目標(biāo)是設(shè)計(jì)高內(nèi)聚的模塊。在面向數(shù)據(jù)的設(shè)計(jì)中,數(shù)據(jù)是優(yōu)先考慮的,例如,在Jackson方法中,首先確定數(shù)據(jù)結(jié)構(gòu),然后將操作分配到數(shù)據(jù)結(jié)構(gòu)上。
面向操作的設(shè)計(jì)的缺點(diǎn)在于它集中于操作,而忽略了數(shù)據(jù)的重要性。面向數(shù)據(jù)的設(shè)計(jì)同樣過分強(qiáng)調(diào)數(shù)據(jù),而忽略了操作的重要性。解決方案是運(yùn)用面向?qū)ο笤O(shè)計(jì)技術(shù),它同等地對(duì)待操作和數(shù)據(jù)。
11.1設(shè)計(jì)在軟件生命周期中的作用
1.什么是設(shè)計(jì)
面向?qū)ο蟮脑O(shè)計(jì)是對(duì)系統(tǒng)對(duì)象的詳細(xì)描述,這些對(duì)象通過相互協(xié)作滿足系統(tǒng)需求。設(shè)計(jì)所描述的仍然是解決方案,只不過在更細(xì)的層面上,描述了實(shí)例變量、方法參數(shù)、返回類型以及各種技術(shù)的細(xì)節(jié)。
因?yàn)樵O(shè)計(jì)和分析具有共同的目標(biāo),所以設(shè)計(jì)建模所使用的圖與分析階段一樣。順序圖描述了對(duì)象之間的交互,類圖描述了結(jié)構(gòu)、行為以及特定類型的對(duì)象之間所共有的關(guān)系。在添加了這么多的細(xì)節(jié)之后,設(shè)計(jì)階段的圖會(huì)變得更龐大,也更復(fù)雜。
在設(shè)計(jì)階段,將構(gòu)造系統(tǒng),并獲得實(shí)現(xiàn)所有需求(包括非功能性需求和其他約束)的系統(tǒng)組織(包括系統(tǒng)構(gòu)架)。需求分析結(jié)果(即分析模型)是設(shè)計(jì)的基本輸入(參見表11-1)。分析模型提供了對(duì)需求的詳細(xì)理解。更重要的是,分析模型提供了一個(gè)在構(gòu)造系統(tǒng)時(shí)需要盡可能保持的系統(tǒng)結(jié)構(gòu)。尤其是,設(shè)計(jì)的目的在于:
深入理解與非功能性需求和約束相聯(lián)系的編程語言、構(gòu)件重用、操作系統(tǒng)、分布與并發(fā)技術(shù)、數(shù)據(jù)庫(kù)技術(shù)、用戶界面技術(shù)、事務(wù)管理技術(shù)等相關(guān)問題;
通過對(duì)單個(gè)子系統(tǒng)、接口和類的需求捕獲,為后續(xù)的實(shí)現(xiàn)活動(dòng)創(chuàng)建適當(dāng)?shù)妮斎牒统霭l(fā)點(diǎn);
能夠把實(shí)現(xiàn)工作劃分成更易于管理的各個(gè)部分,而且盡可能并發(fā)地由不同的開發(fā)組去開發(fā)。這一點(diǎn)在無法基于需求獲取(包括用例模型)或分析(包括分析模型)的結(jié)果來劃分實(shí)現(xiàn)工作時(shí)是很有用的。例如,當(dāng)不易獲取需求和分析結(jié)果時(shí)就是如此;
在軟件生命周期的早期捕獲子系統(tǒng)之間的主要接口。這一點(diǎn)在理解系統(tǒng)構(gòu)架和使用接口作為保持不同開發(fā)組之間同步的手段時(shí)都是很有用的;
通過使用通用的符號(hào),可以可視化地刻畫和思考設(shè)計(jì);
建立對(duì)系統(tǒng)實(shí)現(xiàn)的無縫抽象,把實(shí)現(xiàn)看成是設(shè)計(jì)的直接精化。它不改變結(jié)構(gòu),只填入“血肉”。這使得應(yīng)用代碼生成以及在設(shè)計(jì)和實(shí)現(xiàn)之間的雙向工程等技術(shù)成為可能。
下面來討論設(shè)計(jì)工作流(如圖11-1所示)。
圖11-1包含在設(shè)計(jì)中的工作人員和制品
2.設(shè)計(jì)在軟件生命周期中的作用
設(shè)計(jì)工作集中在細(xì)化階段的末期到構(gòu)造階段的初期(如圖11-2所示),將產(chǎn)生合理且穩(wěn)定的構(gòu)架,并創(chuàng)建實(shí)現(xiàn)模型的藍(lán)圖。在隨后的構(gòu)造階段,當(dāng)系統(tǒng)構(gòu)架已經(jīng)穩(wěn)定并且需求已經(jīng)很好地理解后,重點(diǎn)將轉(zhuǎn)向系統(tǒng)的實(shí)現(xiàn)。
設(shè)計(jì)模型非常接近實(shí)際的系統(tǒng),在整個(gè)軟件生命周期里很自然地要保持并維護(hù)好設(shè)計(jì)模型,尤其在雙向工程中,設(shè)計(jì)模型可用來可視化地刻畫系統(tǒng)實(shí)現(xiàn)并支持圖形化編程技術(shù)。
圖11-2設(shè)計(jì)的焦點(diǎn)
用活動(dòng)圖來說明設(shè)計(jì)工作流,如圖11-3所示。設(shè)計(jì)模型和實(shí)施模型的創(chuàng)建是由構(gòu)架設(shè)計(jì)師啟動(dòng)的,他們勾畫出實(shí)施模型的節(jié)點(diǎn)、設(shè)計(jì)模型中的主要子系統(tǒng)及其接口、包括主動(dòng)類在內(nèi)的重要的設(shè)計(jì)類以及通用的設(shè)計(jì)機(jī)制等。然后,用例工程師通過構(gòu)件工程師參與的設(shè)計(jì)類或子系統(tǒng)及其接口來實(shí)現(xiàn)每個(gè)用例。用例實(shí)現(xiàn)的結(jié)果規(guī)定了參與用例實(shí)現(xiàn)的每個(gè)類和子系統(tǒng)的行為需求,并由構(gòu)件工程師進(jìn)行說明。通過創(chuàng)建每個(gè)類的一致的操作、屬性和關(guān)系,或通過創(chuàng)建每個(gè)子系統(tǒng)所提供的接口的一致的操作,把這些需求集成到每個(gè)類中。
11.2設(shè)計(jì)工作流
圖11-3設(shè)計(jì)中包括參與的工作人員及其活動(dòng)的工作流
在設(shè)計(jì)工作流的整個(gè)過程中,隨著設(shè)計(jì)模型的演化,開發(fā)人員要識(shí)別新的子系統(tǒng)、接口、類和通用的設(shè)計(jì)機(jī)制的候選方案,負(fù)責(zé)子系統(tǒng)的構(gòu)件工程師要精化和維護(hù)這些子系統(tǒng)。
1.設(shè)計(jì)構(gòu)架
設(shè)計(jì)構(gòu)架的目的是通過對(duì)如下內(nèi)容的識(shí)別來勾畫設(shè)計(jì)和實(shí)施模型及其構(gòu)架:
節(jié)點(diǎn)及其網(wǎng)絡(luò)配置;
子系統(tǒng)及其接口;
對(duì)構(gòu)架有重要意義的設(shè)計(jì)類,如主動(dòng)類;
處理共性需求的通用設(shè)計(jì)機(jī)制,如在對(duì)分析類和用例實(shí)現(xiàn)——分析的分析過程中捕獲的系統(tǒng)持久性、分布特征、性能等方面的特殊需求。
如圖11-4所示,構(gòu)架設(shè)計(jì)師要考慮各種重用的可能性,比如可重用相似系統(tǒng)的一部分或通用的軟件產(chǎn)品。要把所設(shè)計(jì)的子系統(tǒng)、接口和其他設(shè)計(jì)要素都合并到設(shè)計(jì)模型中。構(gòu)架設(shè)計(jì)師還要維護(hù)、精化和更新構(gòu)架描述以及設(shè)計(jì)模型和實(shí)施模型的構(gòu)架視圖。
圖11-4設(shè)計(jì)構(gòu)架的輸入和結(jié)果
2.設(shè)計(jì)用例
設(shè)計(jì)用例的目的是為了:
識(shí)別設(shè)計(jì)類或子系統(tǒng),其實(shí)例需要去執(zhí)行用例的事件流;
把用例的行為分布到有交互作用的設(shè)計(jì)對(duì)象或所參與的子系統(tǒng);
定義對(duì)設(shè)計(jì)對(duì)象或子系統(tǒng)及其接口的操作需求;
為用例捕獲實(shí)現(xiàn)性需求。
3.設(shè)計(jì)類
設(shè)計(jì)類的目的是為了創(chuàng)建一個(gè)設(shè)計(jì)類;該設(shè)計(jì)類能夠?qū)崿F(xiàn)其在用例實(shí)現(xiàn)中以及非功能性需求中所扮演的角色,如圖11-5所示。這包括維護(hù)該設(shè)計(jì)類自身及以下方面的內(nèi)容。
圖11-5設(shè)計(jì)類的輸入和結(jié)果
操作;
屬性;
所參與的關(guān)系;
實(shí)現(xiàn)操作的方法;
強(qiáng)制狀態(tài);
對(duì)任何通用設(shè)計(jì)機(jī)制的依賴;
與實(shí)現(xiàn)相關(guān)的需求;
需要提供的接口的正確實(shí)現(xiàn)。
4.設(shè)計(jì)子系統(tǒng)
設(shè)計(jì)子系統(tǒng)的目的如下:
圖11-6設(shè)計(jì)子系統(tǒng)的輸入和結(jié)果
確保該子系統(tǒng)盡可能地獨(dú)立于別的子系統(tǒng)或它們的接口;
確保該子系統(tǒng)提供正確的接口;
確保該子系統(tǒng)實(shí)現(xiàn)其目標(biāo),即提供其接口所定義操作的正確實(shí)現(xiàn);
設(shè)計(jì)的主要結(jié)果是設(shè)計(jì)模型,設(shè)計(jì)模型受分析模型的影響,它要極力保持系統(tǒng)的結(jié)構(gòu),并作為實(shí)現(xiàn)的藍(lán)圖。
設(shè)計(jì)模型包括如下一些元素:
設(shè)計(jì)子系統(tǒng)和服務(wù)子系統(tǒng)以及它們的依賴關(guān)系、接口和內(nèi)容;
設(shè)計(jì)類,包括主動(dòng)類以及它們的操作、屬性、關(guān)系和實(shí)現(xiàn)性需求;
用例實(shí)現(xiàn)—設(shè)計(jì),描述如何用設(shè)計(jì)模型內(nèi)的協(xié)作關(guān)系來設(shè)計(jì)用例;
設(shè)計(jì)模型的構(gòu)架視圖,包括對(duì)構(gòu)架有重要意義的元素;
設(shè)計(jì)還產(chǎn)生實(shí)施模型,以描述系統(tǒng)分布應(yīng)具備的網(wǎng)絡(luò)配置。
實(shí)施模型包括:
節(jié)點(diǎn)以及它們的特征和連接關(guān)系;
主動(dòng)類到節(jié)點(diǎn)的初步映射;
實(shí)施模型的構(gòu)架視圖,包括對(duì)構(gòu)架有重要意義的元素;
設(shè)計(jì)模型和實(shí)施模型被看成是后續(xù)的實(shí)現(xiàn)和測(cè)試活動(dòng)的主要輸入,特別要注意的是:
設(shè)計(jì)子系統(tǒng)和服務(wù)子系統(tǒng)將由包含實(shí)際構(gòu)件(例如源代碼文件、腳本、二進(jìn)制代碼、可執(zhí)行程序等)的實(shí)現(xiàn)子系統(tǒng)來實(shí)現(xiàn)。這些實(shí)現(xiàn)子系統(tǒng)將一對(duì)一地(同構(gòu)地)對(duì)應(yīng)到設(shè)計(jì)子系統(tǒng)。
設(shè)計(jì)類將由包含源代碼的文件構(gòu)件來實(shí)現(xiàn)。在單個(gè)的文件構(gòu)件中實(shí)現(xiàn)多個(gè)設(shè)計(jì)類是常見的(雖然這要依賴于所采用的編程語言)。同樣地,在創(chuàng)建可執(zhí)行構(gòu)件時(shí),承擔(dān)繁重處理任務(wù)的主動(dòng)類將作為一個(gè)輸入。
當(dāng)按很小的、可管理的步驟來規(guī)劃和實(shí)施實(shí)現(xiàn)工作時(shí),將使用用例實(shí)現(xiàn)—設(shè)計(jì)來產(chǎn)生一系列的“構(gòu)造”。每個(gè)構(gòu)造主要實(shí)現(xiàn)一組用例實(shí)現(xiàn)或其中的一部分。
當(dāng)采用把可執(zhí)行構(gòu)件實(shí)施到節(jié)點(diǎn)上的方法來對(duì)系統(tǒng)進(jìn)行分布時(shí),將用到實(shí)施模型和網(wǎng)絡(luò)配置。
11.3設(shè)計(jì)模式
設(shè)計(jì)模式是對(duì)軟件設(shè)計(jì)中普遍出現(xiàn)的一類問題的解決方案,這種解決方案定義明確,文檔充分,經(jīng)歷時(shí)間考驗(yàn)。每種模式都由以下幾部分組成:名稱、問題描述、解決方案、結(jié)果討論、示例實(shí)現(xiàn)、以及相關(guān)模式列表。
11.3.1設(shè)計(jì)原則
為什么要提倡設(shè)計(jì)模式呢?根本原因是為了代碼復(fù)用,增加可維護(hù)性。那么,怎么才能實(shí)現(xiàn)代碼復(fù)用呢?在封裝變化、針對(duì)接口編程而不是實(shí)現(xiàn)編程、多用組合少用繼承等基本原則的基礎(chǔ)上,面向?qū)ο筇岢隽艘恍┰O(shè)計(jì)原則:開放-封閉原則(OpenClosedPrinciple,OCP)、LisKov替換原則(LiskovSubstitutionPrinciple,LSP)、依賴倒置原則(DependencyInversionPrinciple,DIP)、接口隔離原則(InterfaceSegregationPrinciple,ISP)、合成/聚合復(fù)用原則(Composite/AggregateReusePrinciple,CARP)、
最小知識(shí)原則(PrincipleofLeastKnowledge,PLK,也叫迪米特法則)、單一職責(zé)原則(SingleResponsibilityPrinciple,SRP)等。其中,開放-封閉原則具有理想主義的色彩,它是面向?qū)ο笤O(shè)計(jì)的終極目標(biāo)。其他幾條,可以看做是開放-封閉原則的實(shí)現(xiàn)方法。
1.開放-封閉原則(OCP原則)
開放-封閉原則是指“模塊應(yīng)對(duì)擴(kuò)展開放,而對(duì)修改關(guān)閉?!?/p>
對(duì)擴(kuò)展是開放的(OpenforExtension),這意味著模塊的行為是可以擴(kuò)展的。當(dāng)應(yīng)用的需求改變時(shí),可以對(duì)模塊進(jìn)行擴(kuò)展,使其具有滿足那些改變的新行為,也就是說,可以改變模塊的功能。
對(duì)修改是關(guān)閉的(ClosedforModification)。對(duì)模塊行為進(jìn)行擴(kuò)展時(shí),不必改動(dòng)模塊的源代碼或二進(jìn)制代碼。模塊的二進(jìn)制可執(zhí)行版本,無論是可鏈接的庫(kù)、DLL或.EXE文件,都無需改動(dòng)。模塊應(yīng)盡量在不修改原代碼的情況下進(jìn)行擴(kuò)展。
2.LisKov替換原則(LSP原則)
LisKov替換原則是指“子類必須能夠替換基類?!?/p>
如果調(diào)用的是父類的話,那么換成子類也完全可以運(yùn)行?;惪梢猿霈F(xiàn)的地方,子類一定可以出現(xiàn)。原則上子類對(duì)象是可以賦給父類對(duì)象的,也就是說子類可以替換父類,并且出現(xiàn)在父類能夠出現(xiàn)的任何地方。反過來,父類對(duì)象是不能替換子類的,這種特性稱為里氏代換原則,里氏代換原則是繼承復(fù)用的一個(gè)基礎(chǔ),是多態(tài)性的典型表現(xiàn)。
3.依賴倒置原則(DIP原則)
依賴倒置原則是指“高層模塊不應(yīng)該依賴于底層模塊,二者都應(yīng)該依賴于抽象。抽象不應(yīng)該依賴于細(xì)節(jié),細(xì)節(jié)應(yīng)當(dāng)依賴于抽象?!?/p>
通過依賴倒置原則,因?yàn)橛谐橄箢?,就可以針?duì)接口編程,而不是針對(duì)實(shí)現(xiàn)編程;在構(gòu)造對(duì)象時(shí)可以動(dòng)態(tài)地創(chuàng)建各種具體對(duì)象;傳遞參數(shù),或在組合聚合關(guān)系中,盡量引用層次高的類。
4.接口隔離原則(ISP原則)
接口隔離原則是指“不應(yīng)該強(qiáng)迫客戶程序依賴它們不需要使用的方法?!?/p>
每一個(gè)接口應(yīng)該是一種角色,不多不少,不干不該干的事,該干的事都要干。使用多個(gè)專門的接口比使用單一的總接口要好。一個(gè)類對(duì)另外一個(gè)類的依賴性應(yīng)當(dāng)是建立在最小的接口上的。一個(gè)接口代表一個(gè)角色,不應(yīng)當(dāng)將不同的角色都交給一個(gè)接口。沒有關(guān)系的接口合并在一起,形成一個(gè)臃腫的大接口,這是對(duì)角色和接口的污染。
“不應(yīng)該強(qiáng)迫客戶依賴于他們不用的方法。接口屬于客戶,不屬于它所在的類層次結(jié)構(gòu)?!蓖ㄋ椎卣f,不要強(qiáng)迫客戶使用他們不使用的方法,如果強(qiáng)迫用戶使用他們不使用的方法,那么這些客戶就會(huì)面臨由于這些不使用的方法的改變所帶來的改變。
5.合成/聚合復(fù)用原則(CARP原則)
合成/聚合復(fù)用原則是指“要盡量使用合成/聚合,盡量不要使用繼承?!?/p>
在一個(gè)新的對(duì)象里面使用一些已有的對(duì)象,使之成為新對(duì)象的一部分:新的對(duì)象通過對(duì)這些對(duì)象的委派達(dá)到復(fù)用已有功能的目的。繼承之間的依賴關(guān)系限制了靈活性,并最終限制了復(fù)用性,因此,在設(shè)計(jì)時(shí)要多用組合,少用繼承。
6.最小知識(shí)原則(PLK原則)
最小知識(shí)原則是指“不要和陌生人說話?!?/p>
一個(gè)對(duì)象應(yīng)當(dāng)對(duì)其他對(duì)象有盡可能少的了解。體現(xiàn)在類的設(shè)計(jì)上就是要優(yōu)先考慮將一個(gè)類設(shè)置成不變類,盡量降低一個(gè)類的訪問權(quán)限,謹(jǐn)慎使用Serializable,盡量降低成員的訪問權(quán)限。
7.單一職責(zé)原則(SRP原則)
單一職責(zé)原則是指“應(yīng)該只有一個(gè)職責(zé)?!?/p>
一個(gè)類,只有一個(gè)引起變化的原因,應(yīng)該只有一個(gè)職責(zé)。每個(gè)職責(zé)都是變化的一個(gè)軸線,如果一個(gè)類有一個(gè)以上的職責(zé),這些職責(zé)就耦合在了一起,這會(huì)導(dǎo)致脆弱的設(shè)計(jì)。當(dāng)一個(gè)職責(zé)發(fā)生變化時(shí),可能會(huì)影響其他的職責(zé)。另外,多個(gè)職責(zé)耦合在一起,會(huì)影響復(fù)用性。
設(shè)計(jì)模式就是應(yīng)用了這些原則,從而達(dá)到了代碼復(fù)用、增加可維護(hù)性的目的。
11.3.2模式簡(jiǎn)介
設(shè)計(jì)模式在粒度與抽象層次上各不相同,存在眾多的設(shè)計(jì)模式,根據(jù)應(yīng)用目的不同可分為創(chuàng)建型模式、結(jié)構(gòu)型模式和行為型模式三種類型,根據(jù)使用范圍不同可分為類模式和對(duì)象模式兩種類型,如表11-2所示。
創(chuàng)建型模式是對(duì)類的實(shí)例化過程的抽象化,分為類的創(chuàng)建型模式和對(duì)象的創(chuàng)建型模式。類的創(chuàng)建型模式使用繼承關(guān)系把類的創(chuàng)建過程延遲到子類,從而封裝了客戶端將得到哪些具體類的信息,并且隱藏了這些類的實(shí)例是如何創(chuàng)建和組合在一起的。對(duì)象的創(chuàng)建型模式把對(duì)象的創(chuàng)建過程動(dòng)態(tài)的委派給另一個(gè)對(duì)象,從而動(dòng)態(tài)的決定客戶端將得到哪些具體類的實(shí)例,以及這些類的實(shí)例是如何創(chuàng)建和組合在一起的。創(chuàng)建型模式包括單例模式、抽象工廠模式、建造者模式、工廠方法模式、原型模式。
結(jié)構(gòu)型模式描述如何將類或類的對(duì)象結(jié)合在一起形成更大的結(jié)構(gòu)。結(jié)構(gòu)型模式描述兩種不同的東西:類與類的實(shí)例。結(jié)構(gòu)型模式可以分為:類的結(jié)構(gòu)型模式和對(duì)象的結(jié)構(gòu)型模式兩種。類的結(jié)構(gòu)型模式使用繼承來把類、接口等組合在一起,以形成更大的結(jié)構(gòu)。類的結(jié)構(gòu)型模式是靜態(tài)的,比如類形式的適配器模式。對(duì)象的結(jié)構(gòu)型模式描述如何把不同類型的對(duì)象組合在一起,以實(shí)現(xiàn)新的功能的方法。對(duì)象的結(jié)構(gòu)型模式是動(dòng)態(tài)的,比如代理模式。結(jié)構(gòu)型模式包括適配器模式、橋接模式、裝飾模式、組合模式、外觀模式、享元模式、代理模式。
行為型模式主要是責(zé)任和算法的抽象化。行為型模式不僅僅是關(guān)于類和對(duì)象的,而且是關(guān)于它們之間的相互作用的。行為型模式分為類的行為型模式和對(duì)象的行為型模式兩種。類的行為型模式使用繼承關(guān)系在幾個(gè)類之間分配行為。對(duì)象的行為型模式則使用對(duì)象的聚合來分配行為。行為型模式包括模版方法模式、命令模式、迭代器模式、觀察者模式、中介者模式、備忘錄模式、解釋器模式、狀態(tài)模式、策略模式、職責(zé)鏈模式、訪問者模式。
這些設(shè)計(jì)模式之間是互相關(guān)聯(lián)的,其關(guān)系如圖11-7所示。
圖11-7設(shè)計(jì)模式之間的關(guān)系
下面按字母順序簡(jiǎn)單介紹各種設(shè)計(jì)模式。
AbstractFactory(抽象工廠模式):提供一個(gè)創(chuàng)建一系列相關(guān)或相互依賴對(duì)象的接口,而無需指定它們具體的類;
?Adapter(適配器模式):將一個(gè)類的接口轉(zhuǎn)換成客戶希望的另外一個(gè)接口。Adapter模式使得原本由于接口不兼容而不能一起工作的那些類可以一起工作;
Bridge(橋接模式):將抽象部分與它的實(shí)現(xiàn)部分分離,使它們都可獨(dú)立地變化;
Builder(建造者模式):將一個(gè)復(fù)雜對(duì)象的構(gòu)建與它的表示分離,使得同樣的構(gòu)建過程可以創(chuàng)建不同的表示;
?ChainofResponsibility(職責(zé)鏈模式):為解除請(qǐng)求的發(fā)送者和接收者之間耦合,而使多個(gè)對(duì)象都有機(jī)會(huì)處理這個(gè)請(qǐng)求。將這些對(duì)象連成一條鏈,并沿著這條鏈傳遞該請(qǐng)求,直到有一個(gè)對(duì)象處理它;
Command(命令模式):將一個(gè)請(qǐng)求封裝為一個(gè)對(duì)象,從而使你可用不同的請(qǐng)求對(duì)客戶進(jìn)行參數(shù)化;對(duì)請(qǐng)求排隊(duì)或記錄請(qǐng)求日志,以及支持可取消的操作;
Composite(組合模式):將對(duì)象組合成樹形結(jié)構(gòu)以表示“整體—部分”的層次結(jié)構(gòu)。它使得客戶對(duì)單個(gè)對(duì)象和復(fù)合對(duì)象的使用具有一致性;
Decorator(裝飾模式):動(dòng)態(tài)地給一個(gè)對(duì)象添加一些額外的職責(zé)。就擴(kuò)展功能而言,它比生成子類方式更靈活;
Facade(外觀模式):為子系統(tǒng)中的一組接口提供一個(gè)一致的界面,外觀模式定義了一個(gè)高層接口,這個(gè)接口使得該子系統(tǒng)更容易使用;
?FactoryMethod(工廠方法模式):定義一個(gè)用于創(chuàng)建對(duì)象的接口,讓子類決定將哪一個(gè)類實(shí)例化。工廠方法模式將類的實(shí)例化延遲到其子類;
Flyweight(享元模式):運(yùn)用共享技術(shù)有效地支持大量細(xì)粒度的對(duì)象;
Interpreter(解釋器模式):給定一個(gè)語言,定義它的文法的一種表示,并定義一個(gè)解釋器,該解釋器使用該表示來解釋語言中的句子;
Iterator(迭代器模式):提供一種方法順序訪問一個(gè)聚合對(duì)象中各個(gè)元素,而又不需暴露該對(duì)象的內(nèi)部表示;
Mediator(中介模式):用一個(gè)中介對(duì)象來封裝一系列的對(duì)象交互。中介者使各對(duì)象不需要顯式地相互引用,從而使其耦合松散,而且可以獨(dú)立地改變它們之間的交互;
Memento(備忘錄模式):在不破壞封裝性的前提下,捕獲一個(gè)對(duì)象的內(nèi)部狀態(tài),并在該對(duì)象之外保存該狀態(tài)。這樣以后就可將該對(duì)象恢復(fù)到保存的狀態(tài);
?Observer(觀察者模式):定義對(duì)象間的一種一對(duì)多的依賴關(guān)系,以便當(dāng)一個(gè)對(duì)象的狀態(tài)發(fā)生改變時(shí),所有依賴于它的對(duì)象都得到通知并自動(dòng)刷新;
Prototype(原型模式):用原型實(shí)例指定創(chuàng)建對(duì)象的種類,并且通過拷貝該原型來創(chuàng)建新的對(duì)象;
Proxy(代理模式):為對(duì)象提供一個(gè)代理以控制對(duì)該對(duì)象的訪問;
Singleton(單例模式):保證一個(gè)類僅有一個(gè)實(shí)例,并提供一個(gè)訪問它的全局訪問點(diǎn);
State(狀態(tài)模式):允許一個(gè)對(duì)象在其內(nèi)部狀態(tài)改變時(shí)改變其行為。對(duì)象看起來似乎修改了它所屬的類;
Strategy(策略模式):定義一系列的算法,把它們一個(gè)個(gè)封裝起來,并且使它們可互相替換。策略模式使得算法的變化可以獨(dú)立于使用它的客戶;
TemplateMethod(模板方法模式):定義一個(gè)操作中的算法的骨架,但將一些步驟延遲到子類中。模板方法模式使得子類可以不改變一個(gè)算法的結(jié)構(gòu)即可重新定義該算法的某些特定步驟;
Visitor(訪問者模式):表示一個(gè)作用于某對(duì)象結(jié)構(gòu)中的各元素的操作。訪問者模式可以在不改變各元素的類的前提下定義作用于這些元素的新操作。
11.3.3設(shè)計(jì)模式的優(yōu)勢(shì)與應(yīng)用
學(xué)習(xí)和使用設(shè)計(jì)模式可以改變?cè)O(shè)計(jì)軟件的方式,加深對(duì)面向?qū)ο罄碚摰睦斫?。設(shè)計(jì)模式在以下兩個(gè)方面幫助開發(fā)人員設(shè)計(jì)出更好的軟件:
設(shè)計(jì)模式為協(xié)作和文檔提供了通用語言;
設(shè)計(jì)模式深化了面向?qū)ο蟮睦碚摗?/p>
1.通用語言
學(xué)習(xí)設(shè)計(jì)模式必須有耐心和毅力。首先,有許多種模式要學(xué)習(xí);其次,每一種模式都需要時(shí)間來消化、理解。但是,一旦了解到坐在桌子對(duì)面的開發(fā)人員使用通用語言,原本需要三個(gè)小時(shí)的設(shè)計(jì)討論,因?yàn)槭褂迷O(shè)計(jì)模式作為交流的基礎(chǔ),甚至可以壓縮到短短的15分鐘,就會(huì)投入設(shè)計(jì)模式的學(xué)習(xí)和使用當(dāng)中。一群理解設(shè)計(jì)模式的開發(fā)人員能夠使用通用語言進(jìn)行交流,這種方式既富有表達(dá)能力又非常簡(jiǎn)潔。
設(shè)計(jì)模式能夠幫助開發(fā)人員高效且快速地進(jìn)行交流。任何置疑都可以通過查詢已被廣泛認(rèn)可的資料來加以澄清,這種做法在設(shè)計(jì)會(huì)議、設(shè)計(jì)文檔以及代碼注釋中都行之有效。
2.深化面向?qū)ο罄碚?/p>
對(duì)大多數(shù)的開發(fā)人員來說,面向?qū)ο蟮睦碚摵蛯?shí)踐都不是憑直覺就能夠得到的,它要求以抽象的、分析的、有創(chuàng)造力的方式進(jìn)行思考—而且要同時(shí)做到這三者。設(shè)計(jì)模式將面向?qū)ο蟮膶?shí)踐應(yīng)用于各種清晰定義的問題上,是學(xué)習(xí)面向?qū)ο笤O(shè)計(jì)的最佳案例,仔細(xì)研究不同的決定所產(chǎn)生的各種結(jié)果,以及相同的技術(shù)如何以完全不同的方式使用有助于掌握面向?qū)ο蟮睦碚摗?/p>
3.應(yīng)用
現(xiàn)在,可以找到大量設(shè)計(jì)模式有關(guān)的資料。開發(fā)人員為修改和擴(kuò)充一大批設(shè)計(jì)模式投入了大量的時(shí)間、精力和專業(yè)知識(shí)。
設(shè)計(jì)模式能夠很好地應(yīng)用于嚴(yán)格定義的問題上。在分析和構(gòu)架的建立過程中,已經(jīng)識(shí)別出大量問題等待解決。在許多情況下,能夠?qū)⒁幌盗械脑O(shè)計(jì)模式應(yīng)用于一個(gè)包或一組包上,每種模式都能幫助提供一些功能或?qū)崿F(xiàn)一個(gè)設(shè)計(jì)目標(biāo)。
11.4規(guī)劃設(shè)計(jì)工作
要想取得成功,設(shè)計(jì)是一個(gè)一致且統(tǒng)一的努力過程。但是,設(shè)計(jì)從本質(zhì)上來說是一個(gè)間斷的、不斷推進(jìn)的過程。為了完成設(shè)計(jì),開發(fā)人員分為多個(gè)小團(tuán)隊(duì),甚至是相互隔離的。然后,每個(gè)團(tuán)隊(duì)或個(gè)人就專心于新技術(shù)的細(xì)節(jié)和面向?qū)ο笤O(shè)計(jì)的挑戰(zhàn)。因?yàn)樵O(shè)計(jì)人員必須努力奮斗才能在復(fù)雜的情況中理出頭緒,所以全神貫注于自己所負(fù)責(zé)的一部分而將其他事情排除在外是非常自然的。
一旦開始設(shè)計(jì),每個(gè)設(shè)計(jì)工作都會(huì)持續(xù)一段時(shí)間。如果沒有認(rèn)識(shí)到這一點(diǎn),就會(huì)造成進(jìn)展緩慢并為此痛苦不堪。為保證自己的工作與他人的工作相吻合,開發(fā)人員要不斷地查看整個(gè)系統(tǒng)的全貌。為模擬一致且統(tǒng)一的工作過程,在將開發(fā)人員分工并允許其投入到自己的那部分工作之前,應(yīng)該先建立起整個(gè)設(shè)計(jì)的目標(biāo)。設(shè)計(jì)過程的步驟如下:
建立整個(gè)設(shè)計(jì)目標(biāo);
建立設(shè)計(jì)準(zhǔn)則;
尋找獨(dú)立的設(shè)計(jì)工作。
11.4.1建立整個(gè)設(shè)計(jì)目標(biāo)
每個(gè)大型系統(tǒng)的建立都經(jīng)歷上百萬次決策,其中許許多多的決策與其說是才氣迸發(fā)、靈光閃現(xiàn)時(shí)發(fā)現(xiàn)的完美真理,不如說是對(duì)各種設(shè)計(jì)因素反復(fù)權(quán)衡、折衷所得到的結(jié)果。因?yàn)樵O(shè)計(jì)是針對(duì)需求和構(gòu)架進(jìn)行的,所以對(duì)設(shè)計(jì)來說,在做出決定之前,首先建立起清晰的設(shè)計(jì)目標(biāo)將有助于保持系統(tǒng)的一致性,并且能夠使決策更加輕松。
1.清晰度
對(duì)任何一個(gè)設(shè)計(jì)來說,清晰度和可理解性都是至關(guān)重要的,開發(fā)人員無法評(píng)審并實(shí)現(xiàn)無法理解的設(shè)計(jì)方案。面對(duì)一個(gè)晦澀難懂的設(shè)計(jì),絕大多數(shù)的開發(fā)人員通常會(huì)采取兩種態(tài)度:努力遵循設(shè)計(jì),最終寫出一堆令人迷惑的代碼;或直接將設(shè)計(jì)置之一旁。與此相反,清晰而無二義性的設(shè)計(jì)通常能夠帶來易于維護(hù)和擴(kuò)展的代碼。
保持類中的方法以及包中的類的強(qiáng)內(nèi)聚能夠提高清晰度。包之間的松耦合能夠使包之間的接口簡(jiǎn)潔并易于理解,封裝提高可讀性,這樣,要使用一個(gè)類只需了解和掌握有限的內(nèi)容即可。
2.性能和可靠性
許多系統(tǒng)在性能和可靠性方面有明確的需求。在大多數(shù)情況下,性能和可靠性方面的目標(biāo)通過采用正確的技術(shù)、針對(duì)技術(shù)優(yōu)勢(shì)來設(shè)計(jì)就可以滿足性能和可靠性。開發(fā)人員必須理解該技術(shù)如何在系統(tǒng)的不同層次之間實(shí)現(xiàn)數(shù)據(jù)交換,以及該技術(shù)如何保證數(shù)據(jù)的完整性。如果在設(shè)計(jì)過程的早期就建立了性能和可靠性方面的目標(biāo),就能鼓勵(lì)開發(fā)人員早早考慮這些問題,這比拖延或者夢(mèng)想奇跡出現(xiàn)要好得多。
3.可擴(kuò)展性
由于機(jī)構(gòu)的需求不斷變化,系統(tǒng)必須能夠適應(yīng)新的環(huán)境。所以,可擴(kuò)展性幾乎永遠(yuǎn)是需要優(yōu)先考慮的,即使在客戶沒有意識(shí)到這一點(diǎn)的時(shí)候也是一樣。
通常強(qiáng)內(nèi)聚和松耦合使那些將來有可能需要改動(dòng)的類被歸到同一個(gè)包中,而且這樣的包與系統(tǒng)的其他部分保持松耦合更有可能,這樣就能夠有效地限制每個(gè)改動(dòng)對(duì)系統(tǒng)的影響。
如果能夠確定哪些部分非常有可能發(fā)生變化,那么可以將變化封裝到可交換的子系統(tǒng)中,或?qū)⑾到y(tǒng)設(shè)計(jì)為使用可配置的數(shù)據(jù)來處理這些變化,就能夠?qū)⒖勺冃栽O(shè)計(jì)進(jìn)系統(tǒng)中。當(dāng)然,這要求非常準(zhǔn)確地把握系統(tǒng)的未來愿景,或者說,就是要有先見之明。如果估計(jì)錯(cuò)了,那不但浪費(fèi)時(shí)間,而且會(huì)增加系統(tǒng)的復(fù)雜度。
4.重用潛力
類的復(fù)用—既包括在項(xiàng)目?jī)?nèi)的也包括在項(xiàng)目間的,是面向?qū)ο蠹夹g(shù)的一個(gè)巨大賣點(diǎn)??芍赜玫念惐仨毦哂型ㄓ眯缘某橄蠛头庋b良好的數(shù)據(jù)。當(dāng)以重用為目標(biāo)時(shí),要保持類的體積小巧且定義明確而集中。另外,想想那些要使用或修改類的開發(fā)人員,要想減輕其負(fù)擔(dān),必須將依賴關(guān)系降至最小,而且要使抽象易于理解和使用。
11.4.2建立設(shè)計(jì)準(zhǔn)則
在設(shè)計(jì)過程中,建立整個(gè)項(xiàng)目的設(shè)計(jì)準(zhǔn)則,就能夠統(tǒng)一不同設(shè)計(jì)人員或者團(tuán)隊(duì)的工作。各項(xiàng)設(shè)計(jì)工作都應(yīng)該使用相同的圖表,在相同的細(xì)節(jié)層次上描述解決方案,并且遵循相同的命名約定,對(duì)大多數(shù)的項(xiàng)目來說,下面列出的準(zhǔn)則可以作為一個(gè)合理的起點(diǎn)。
1.用例的圖
用幾個(gè)順序圖來描述每個(gè)用例,對(duì)每個(gè)重要的事件流都用一個(gè)順序圖來描述。此外,應(yīng)該用一個(gè)類圖來描述不同順序圖中出現(xiàn)的各個(gè)類之間的關(guān)系。在某些情況下,可以使用狀態(tài)圖來描述特定類的狀態(tài)的相關(guān)行為。
2.細(xì)節(jié)層次
與分析階段相比,設(shè)計(jì)階段所描述的細(xì)節(jié)層次要深入得多。每個(gè)方法都必須明確聲明,包括返回值類型和完整的參數(shù)列表等。
對(duì)于任何在順序圖中出現(xiàn)的對(duì)象,都必須明確其來龍去脈:定位或生成。這些對(duì)象也許出現(xiàn)在同一個(gè)順序圖中,也許出現(xiàn)在其他起支撐作用的順序圖中。在分析階段,順序圖通常是一些不顯眼的支撐序列,所有的對(duì)象在需要的時(shí)候自動(dòng)出現(xiàn)。在設(shè)計(jì)階段,必須生成每個(gè)對(duì)象,保存下來以做將來之用,最終要銷毀掉。
3.命名約定
用精心選擇的動(dòng)詞或動(dòng)詞與名詞的組合來為方法命名,Java類庫(kù)中有許多優(yōu)秀的命名范例,例如paint和open等。如果有返回類型,方法的名稱必須與之相符合。例如,某個(gè)方法返回一個(gè)指向TimeCard對(duì)象的引用,就可以叫做getTimeCard或getCurrentTimeCard。
應(yīng)當(dāng)用名詞、名詞的組合、或形容詞與名詞的組合來命名類。String、MenuItem以及OutputStream都是Java類庫(kù)中的范例。
對(duì)其他開發(fā)人員來說,每個(gè)類、方法的目的都應(yīng)當(dāng)是明確且無二義性的。
4.內(nèi)聚性
類中的一組方法都應(yīng)該內(nèi)聚成為一個(gè)整體,這就要求它們有共同的目的或職責(zé)。同樣的,每個(gè)包中的類都必須有一個(gè)統(tǒng)一的目標(biāo),不能僅僅為了方便,隨意地或簡(jiǎn)單地將類或方法進(jìn)行分組。
11.4.3尋找獨(dú)立的設(shè)計(jì)工作
為了合理地分配整個(gè)設(shè)計(jì)工作,必須找出與其他部分松耦合的若干包或一組包,這樣才能使負(fù)責(zé)不同任務(wù)的開發(fā)人員在開始獨(dú)立的設(shè)計(jì)活動(dòng)之前,在接口定義上達(dá)成一致。
緊耦合的包必須放在一起進(jìn)行設(shè)計(jì),松耦合并封裝良好的包就可以考慮獨(dú)立開發(fā)。子系統(tǒng)非常適合獨(dú)立開發(fā),因?yàn)樽酉到y(tǒng)的定義就是封裝良好的獨(dú)立體。
進(jìn)行工作分配時(shí)要注意匹配問題,即:每個(gè)獨(dú)立的設(shè)計(jì)工作都必須與承擔(dān)該設(shè)計(jì)工作的團(tuán)隊(duì)的技術(shù)能力相適應(yīng)。有時(shí)候需要將一部分設(shè)計(jì)工作進(jìn)一步劃分為多個(gè)更小的工作,以匹配已有團(tuán)隊(duì)的技術(shù)能力,否則,就需要重新調(diào)整團(tuán)隊(duì)的成員組織,或?qū)ζ溥M(jìn)行培訓(xùn)以提高技能。
11.5設(shè)計(jì)包或子系統(tǒng)
包或子系統(tǒng)的設(shè)計(jì)是建立在分析模型上的,包括類圖和順序圖。雖然每個(gè)包都設(shè)計(jì)和實(shí)現(xiàn)為能夠單獨(dú)交付使用的,但是所有的包都是相互協(xié)作來實(shí)現(xiàn)用例的。在開始包設(shè)計(jì)工作之前,開發(fā)人員首先必須找出哪些用例包含了該包。通過這個(gè)過程,開發(fā)人員更加明確該包與用例中的其他包進(jìn)行的交互,從這個(gè)意義上講,開發(fā)人員必須和其他所有涉及到的包的開發(fā)人員相互合作,才能最終確定各個(gè)包之間的接口。
包或子系統(tǒng)的設(shè)計(jì)同樣也受到系統(tǒng)構(gòu)架和系統(tǒng)總體目標(biāo)的約束。也就是說,系統(tǒng)構(gòu)架決定了包之間的關(guān)系,每當(dāng)包中的類使用了包外的類,就產(chǎn)生了這些包之間的依賴關(guān)系,必須對(duì)這些新的關(guān)系進(jìn)行評(píng)估以確認(rèn)它們是否與系統(tǒng)構(gòu)架相兼容。
每個(gè)包或子系統(tǒng)都有自己的目標(biāo)。例如,由用戶界面類組成的包必須具有高度的靈活性和可擴(kuò)展性,由實(shí)體類組成的包就必須封裝良好且能夠滿足苛刻的性能方面的需求。
每個(gè)設(shè)計(jì)方面的工作都必須遵守下面的步驟。
(1)確立工作目標(biāo)和優(yōu)先級(jí)。雖然整個(gè)系統(tǒng)的設(shè)計(jì)目標(biāo)已經(jīng)確定,但不是每個(gè)設(shè)計(jì)工作都會(huì)影響到目標(biāo)。每項(xiàng)設(shè)計(jì)工作都必須確定要達(dá)到的目標(biāo)和優(yōu)先級(jí),以及它無法實(shí)現(xiàn)的任務(wù)。從涉及到的技術(shù)和包或子系統(tǒng)的目標(biāo)來看,問題就比較清晰。例如,TimeCardDomain包和TimeCardWorkflow包的設(shè)計(jì)工作無疑對(duì)性能有極大的作用,因?yàn)樗鼈兛刂浦志么鎯?chǔ)和數(shù)據(jù)流;而HTMLProduction框架以及TimeCardUI包的設(shè)計(jì)工作對(duì)系統(tǒng)的可擴(kuò)展性有極大的影響,因?yàn)橛脩艚缑嬖谛枨笞兏媲笆欠浅4嗳醯?,極易受到需求變更的影響。
(2)對(duì)前一步工作進(jìn)行評(píng)審。前面的步驟產(chǎn)生了分析模型,選擇了技術(shù),而且建立了考勤系統(tǒng)的結(jié)構(gòu)約束。每部分的設(shè)計(jì)工作都必須經(jīng)過評(píng)審,然后以此為基礎(chǔ),遵循各種約束進(jìn)行設(shè)計(jì)。分析模型從開發(fā)人員的角度對(duì)問題進(jìn)行了描述,是設(shè)計(jì)包和子系統(tǒng)時(shí)最好的資源。在許多情況下,類或包的職責(zé)能夠直接從分析類的職責(zé)演化出來。
(3)針對(duì)目標(biāo)進(jìn)行設(shè)計(jì)。在某些情況下,高層設(shè)計(jì)方案幾乎完全由所采用的技術(shù)決定,例如,采用EJB進(jìn)行開發(fā)將決定設(shè)計(jì)方案中很大的一部分。要想實(shí)現(xiàn)每個(gè)用例的目標(biāo),必須做出一系列決定,實(shí)際上在各種限制條件下,開發(fā)人員無需也沒有機(jī)會(huì)做出大量決定,在某些情況下,完全由開發(fā)人員來設(shè)計(jì)包或子系統(tǒng),以最終實(shí)現(xiàn)系統(tǒng)目標(biāo)。對(duì)這種需要高度創(chuàng)造性和反復(fù)迭代的設(shè)計(jì)工作,設(shè)計(jì)模式絕對(duì)是一個(gè)非常有價(jià)值的技術(shù)。
(4)將設(shè)計(jì)應(yīng)用于用例。將高層設(shè)計(jì)應(yīng)用到用例上,不但能夠驗(yàn)證設(shè)計(jì)方案,而且會(huì)改進(jìn)設(shè)計(jì)。在這個(gè)過程中,逐步將前一步建立起來的高層設(shè)計(jì)方案應(yīng)用到各個(gè)用例上,直到充實(shí)整個(gè)設(shè)計(jì)方案的細(xì)節(jié),而且滿足所有可應(yīng)用的用例,或證明是失敗的。
11.6設(shè)計(jì)工作流:考勤系統(tǒng)實(shí)例研究
考勤系統(tǒng)的設(shè)計(jì)工作可以自然地分為四個(gè)部分:
?TimeCardDomain包和TimeCardWorkflow包;
HtmlProduction框架;
TimeCardUI包;
BillingSystemInterface子系統(tǒng)。
因?yàn)門imeCardDomain包和TimeCardWorkflow包緊密相關(guān),所以應(yīng)當(dāng)放在一起設(shè)計(jì),它們依賴相同的技術(shù),且緊密耦合。
HTMLProduction框架的設(shè)計(jì)應(yīng)當(dāng)獨(dú)立于TimeCardUI包進(jìn)行,在整個(gè)系統(tǒng)范圍內(nèi),是惟一實(shí)際生成HTML頁(yè)面的包,TimeCardUI包使用它,因此,該框架應(yīng)該能夠獨(dú)立地演化、擴(kuò)展。實(shí)現(xiàn)該目的一種方法就是在進(jìn)行TimeCardUI包的設(shè)計(jì)和實(shí)現(xiàn)之前,先建立HTMLProduction框架的一個(gè)最小功能集,可作為構(gòu)架設(shè)計(jì)的骨架。一旦建立了這個(gè)最小的功能集,HTMLProduction就能夠在設(shè)計(jì)和實(shí)現(xiàn)TimeCardUI包的同時(shí)不斷擴(kuò)展、完善,下面將著重介紹這兩部分的設(shè)計(jì)。
很顯然,BillingSystemInterface子系統(tǒng)的設(shè)計(jì)是一個(gè)獨(dú)立的工作,系統(tǒng)的其他部分不依賴于它,因而該包的開發(fā)工作可以并發(fā)進(jìn)行,也可以推遲到有空余的開發(fā)資源時(shí)再進(jìn)行。
11.7HTMLProduction框架
要設(shè)計(jì)一個(gè)漂亮的HTML生成框架的解決方案,必須確定設(shè)計(jì)目標(biāo)。如果沒有一個(gè)定義好的目標(biāo),工作是難以開展的??梢钥紤]對(duì)一個(gè)子系統(tǒng)或框架的目標(biāo)寫一份面向技術(shù)的內(nèi)部需求文檔,系統(tǒng)的其他部分如何和該框架交互,框架能夠提供什么功能,都是要考慮的細(xì)節(jié)。
11.7.1設(shè)計(jì)目標(biāo)
在開始設(shè)計(jì)之前,必須要給出對(duì)應(yīng)于前面定義的目標(biāo)的具體例子以及非常明確的標(biāo)準(zhǔn),清晰的、可量化的目標(biāo)能夠驅(qū)動(dòng)設(shè)計(jì)并提供一個(gè)有價(jià)值的度量標(biāo)準(zhǔn)。定義得模模糊糊的目標(biāo)不僅不能為設(shè)計(jì)提供一個(gè)正確的方向,還會(huì)挫敗開發(fā)人員的積極性。例如,兩個(gè)這樣的標(biāo)準(zhǔn):一是要求框架必須支持新版本IE,另一個(gè)則要求框架具有廣泛的可擴(kuò)展性,第一個(gè)標(biāo)準(zhǔn)顯然更具體,要有用得多。
目標(biāo)1:支持視圖的模塊化結(jié)構(gòu)
如果能夠在一個(gè)HTML組件中嵌入另一個(gè)HTML組件,就可以輕松地得到更復(fù)雜的頁(yè)面。一個(gè)頁(yè)面中可能會(huì)包含表格,輸入表單以及文字。表格中的一個(gè)單元格可能會(huì)包含一個(gè)圖像,而另一個(gè)單元格可能會(huì)包含另一個(gè)完整的表格。組件的嵌套可以讓那些有耐心的開發(fā)人員利用少量的相對(duì)簡(jiǎn)單的構(gòu)件就能得到相當(dāng)復(fù)雜的頁(yè)面。底層的HTML生成類應(yīng)該能實(shí)現(xiàn)這樣的效果,允許表示層開發(fā)人員方便地進(jìn)行結(jié)構(gòu)之間的嵌套和合并。
考慮這樣一個(gè)頁(yè)面,頁(yè)面中包含一個(gè)表格,表格中又包含圖像和文字,這個(gè)稍微復(fù)雜的頁(yè)面是由三個(gè)簡(jiǎn)單的元素組合而成的。下面是該表格的偽代碼表示:
從框架中得到一個(gè)新表格;
從框架中得到一個(gè)新圖像,設(shè)置其源地址;
將圖像添加到表格中;
將文字添加到表格中;
從框架中得到一個(gè)新頁(yè)面;
將表添加到這個(gè)頁(yè)面中。
GUI程序員使用這種“自底向上”的組裝方法才能在生成復(fù)雜界面的同時(shí)保持思路清晰、從容不追。就算是最復(fù)雜的界面,同樣是由一些相對(duì)較小的組件組合而成,要設(shè)計(jì)的框架必須提供相似的功能。
目標(biāo)2:簡(jiǎn)化HTML的生成
設(shè)計(jì)的目標(biāo)是,要讓開發(fā)人員能夠?qū)⒕性趶?fù)雜的業(yè)務(wù)邏輯上,而不是花費(fèi)時(shí)間來考慮如何生成實(shí)際的HTML頁(yè)面的細(xì)節(jié)。表示層開發(fā)人員應(yīng)該能夠輕松地將數(shù)據(jù)添加到視圖上,而不需要知道對(duì)應(yīng)于不同的瀏覽器在顯示上有哪些不同,也不用了解某個(gè)HTML標(biāo)簽具體表示什么意思。
簡(jiǎn)言之,就是要簡(jiǎn)化HTML的生成。這樣,就必須遵循以下三個(gè)準(zhǔn)則:
把實(shí)際的標(biāo)記和選項(xiàng)隱藏起來
除了開發(fā)框架的團(tuán)隊(duì),其他的開發(fā)人員根本不需要了解HTML語法的任何細(xì)節(jié)。
隱藏所有瀏覽器相關(guān)的行為
使用HTML生成框架的應(yīng)用程序和表示邏輯必須能完全忽略瀏覽器相關(guān)的一些行為。開發(fā)人員要相信,底層的生成框架能夠根據(jù)用戶瀏覽器的不同生成特定的HTML頁(yè)面。
支持用戶界面的自然開發(fā)
從用戶界面開發(fā)人員的角度來說,添加內(nèi)容或添加數(shù)據(jù)都應(yīng)該是非常自然的事,不需要了解生成的HTML頁(yè)面的結(jié)構(gòu)。
下面,看一看如何使用一個(gè)視圖,它從應(yīng)用域中抽取數(shù)據(jù),并顯示在表格中。抽取過程如下:
從領(lǐng)域中獲取原始數(shù)據(jù);
將獲得的數(shù)據(jù)格式化成為二維的字符串?dāng)?shù)組;
從請(qǐng)求中獲取用戶的上下文信息,包括瀏覽器類型;
從框架中得到一個(gè)新頁(yè)面;
設(shè)置頁(yè)面的標(biāo)題;
在頁(yè)面中添加一些指示性的文字;
從框架中得到一個(gè)新表格;
設(shè)置表格的列標(biāo)題;
將第2步得到的格式化數(shù)據(jù)設(shè)置為表格數(shù)據(jù);
將表格添加到頁(yè)面中;
請(qǐng)求獲取HTML頁(yè)面。
注意,視圖并不知道HTML頁(yè)面是如何生成的,它僅僅把數(shù)據(jù)連接到由框架提供的元素中。
通過封裝HTML生成器的細(xì)節(jié),可以定義開發(fā)團(tuán)隊(duì)自己的規(guī)范。表示層頁(yè)面開發(fā)人員可以保持?jǐn)?shù)據(jù)操作的業(yè)務(wù)邏輯不受實(shí)際HTML復(fù)雜度的影響,這樣就可以保證更小的代碼庫(kù)且更易于理解。
目標(biāo)3:支持可選項(xiàng)
可選項(xiàng)讓用戶可以按照自己的喜好對(duì)系統(tǒng)的顯示風(fēng)格進(jìn)行修改。例如,用戶希望改變顏色方案,或給表格的每個(gè)單元格加上邊框。這可以通過修改配置文件,然后重啟動(dòng)系統(tǒng)來實(shí)現(xiàn),不應(yīng)該通過修改源代碼來實(shí)現(xiàn),大多數(shù)用戶都希望能夠在系統(tǒng)運(yùn)行中可以通過可選項(xiàng)設(shè)置來改變系統(tǒng)的顯示風(fēng)格。
這里有多種類型的界面元素,每種元素有不同的選項(xiàng)。對(duì)頁(yè)面來說,應(yīng)該允許自定義背景顏色和文字顏色;對(duì)表格來說,應(yīng)該允許自定義顏色、邊框的寬度及不同的對(duì)齊方式。使用選項(xiàng),系統(tǒng)可以得到數(shù)十種甚至數(shù)百種不同的屏幕特性。
可選項(xiàng)也有很多細(xì)節(jié),可以為不同的系統(tǒng)提供不同級(jí)別的定制。例如,面向匿名用戶的系統(tǒng)可能只有一個(gè)顯示風(fēng)格,由系統(tǒng)管理員來定制;而復(fù)雜的企業(yè)內(nèi)部網(wǎng)系統(tǒng),應(yīng)該允許每個(gè)用戶使用自己的可選項(xiàng)參數(shù)覆蓋系統(tǒng)的默認(rèn)參數(shù),得到自己喜愛的顯示風(fēng)格;而有些系統(tǒng)甚至提供一些復(fù)雜的可選項(xiàng)方案供用戶選擇,來覆蓋系統(tǒng)默認(rèn)的顯示風(fēng)格。
要想實(shí)現(xiàn)系統(tǒng)的簡(jiǎn)單性和可重用性,底層的HTML生成框架不應(yīng)該關(guān)注可選項(xiàng)的創(chuàng)建、編輯及實(shí)現(xiàn)等細(xì)節(jié)??蚣茉试S視圖對(duì)象為某個(gè)元素進(jìn)行可選項(xiàng)設(shè)置,至于如何根據(jù)指定的環(huán)境創(chuàng)建正確的選項(xiàng),則由視圖負(fù)責(zé)。如果某個(gè)元素沒有設(shè)置可選項(xiàng),就使用上一級(jí)元素的可選項(xiàng)設(shè)置。對(duì)可選項(xiàng)、框架說明如下:
僅負(fù)責(zé)應(yīng)用可選項(xiàng),不負(fù)責(zé)根據(jù)環(huán)境正確設(shè)置可選項(xiàng);
允許在任何級(jí)別上設(shè)置可選項(xiàng);
要使可選項(xiàng)擴(kuò)展更容易,并能持新的可選項(xiàng)類型。
這樣,框架將在不失去獨(dú)立性和重用潛力的情況下支持可選項(xiàng)的使用。
目標(biāo)4:可擴(kuò)展性和封裝
類庫(kù)使用人員應(yīng)該能夠輕松地?cái)U(kuò)展該框架而不影響已有的視圖,表示邏輯不會(huì)因?yàn)镮E或Netscape的不同版本而有什么改變,變化必須和框架分離。
框架開發(fā)人員可以自由地改變框架以利用新版本瀏覽器提供的功能,或修改顯示異常,或修改系統(tǒng)的顯示風(fēng)格,這些改變不應(yīng)該影響表示層開發(fā)人員所依賴的接口。因此,框架必須能適應(yīng)下面這些改變而不影響已有的客戶代碼或已有的框架代碼。
新的瀏覽器。
改變某個(gè)元素的HTML規(guī)范。
改變某個(gè)HTML元素的默認(rèn)顯示。
表示層應(yīng)該和HTML生成類的變化分離,也就是說,要把HTML生成類保護(hù)起來,不受應(yīng)用層或表示層變化的影響。要想保證這一點(diǎn),HTML生成類只能依賴于基本的和標(biāo)準(zhǔn)的java類。例如,框架不允許知道關(guān)于考勤卡和雇員的任何細(xì)節(jié),用戶界面開發(fā)人員從領(lǐng)域類中抽取所需的數(shù)據(jù),然后才使用數(shù)據(jù)來填充HTML生成類。
只有滿足了這些特殊的設(shè)計(jì)目標(biāo),框架開發(fā)人員才能讓表示層開發(fā)人員感到滿意。另外,封裝使得其他項(xiàng)目重用整個(gè)框架變得更加簡(jiǎn)單。
11.7.2按目標(biāo)進(jìn)行設(shè)計(jì)
一旦定義好了具體的目標(biāo),下一步就是要進(jìn)行高層設(shè)計(jì)。要想同時(shí)滿足所有的目標(biāo)通常很難做到,因此,可以分步設(shè)計(jì),一次只瞄準(zhǔn)一個(gè)目標(biāo),然后定期檢查以確保沒有出現(xiàn)偏差。
1.按目標(biāo)1進(jìn)行設(shè)計(jì):支持視圖的模塊化結(jié)構(gòu)
支持視圖的模塊化結(jié)構(gòu),是HTML生成類的核心,這些類庫(kù)的目的就是要讓開發(fā)人員可以從簡(jiǎn)單的基本類型構(gòu)造出完善的結(jié)構(gòu)。
1)組合設(shè)計(jì)模式
目標(biāo)1實(shí)際上和一個(gè)已有設(shè)計(jì)模式非常吻合。Gamma和其同事這樣描述組合模式的目的:“將對(duì)象組合成樹形結(jié)構(gòu)來表示整體
部分的層次結(jié)構(gòu),組合使用戶可以以統(tǒng)一的方式來對(duì)待單個(gè)對(duì)象和組合對(duì)象”。這樣,可以用下面樹形結(jié)構(gòu)來表示整體
部分的層次結(jié)構(gòu)。
頁(yè)面
表格
圖像
文字
Gamma和其同事用組合圖形來表示該設(shè)計(jì)模式,如線、矩形和圖,這些元素可以組合成復(fù)雜的圖。
2)應(yīng)用組合模式
現(xiàn)在,就考慮如何把組合模式應(yīng)用到HTML生成類庫(kù)中,用組合模式將一些相對(duì)簡(jiǎn)單的HTML生成器組合起來以構(gòu)造復(fù)雜的HTML頁(yè)面。例如,一個(gè)表格中可能包括文字、圖像、表單以及其他表格,一個(gè)頁(yè)面中可能包含表格、表單、圖像和文字,表單中可能會(huì)有輸入?yún)^(qū)域、說明文字以及提交按鈕。需要注意的是,有些元素,像表單和表格,都可以包含其他元素;而另一些元素,像文字,就不能包含其他元素。
要得到這些元素,必須要有一個(gè)組合對(duì)象以及單個(gè)對(duì)象都能夠?qū)崿F(xiàn)的通用接口,這里把接口命名為IHtmlProducer,每個(gè)組合對(duì)象都實(shí)現(xiàn)IHtmlProducer接口定義的方法。圖11-8表示了組合頁(yè)面是怎樣由簡(jiǎn)單的元素組合而來的。注意,其中每一個(gè)組合對(duì)都可以接收IHtmlProducer。
圖11-8使用組合模式來構(gòu)造HTML頁(yè)面
為了保證命名的一致性,為每個(gè)實(shí)現(xiàn)了該接口的類加上Producer后綴。例如,有PageProducer、TextProducer、ImagePrducer和TableProducer,每個(gè)類都會(huì)格式化自己的HTML頁(yè)面。除了TextProducer之外,所有的類都可以包含任何其他IHtmlProducer。因此,生成一個(gè)PageProducer對(duì)象就可以得到一個(gè)HTML頁(yè)面,然后就可以添加一個(gè)TableProducer,接下來,就可以給TableProducer添加ImageProducer和TextProducer。
每種類型的生成器添加新的生成器的方法都各不相同,PageProducer是一個(gè)一個(gè)地添加生成器,而TableProducer允許把不同的生成器添加到不同的單元格上,這樣,可以對(duì)其位置進(jìn)行安排。
盡管每種類型的元素添加數(shù)據(jù)的方法都各不相同,但它們都支持同一種得到格式化HTML頁(yè)面的方式。一旦構(gòu)造了page對(duì)象,得到相應(yīng)的HTML頁(yè)面就變得簡(jiǎn)單了。如圖11-9所示,整體根本不需要了解其部分的詳細(xì)情況。
圖11-9從組合中獲取HTML
圖11-9從組合中獲取HTML
圖11-10簡(jiǎn)單組合的參與類
3)設(shè)計(jì)評(píng)估
下面,用組合模式來描述設(shè)計(jì),并對(duì)有效性進(jìn)行評(píng)估。因?yàn)镻ageProducer和TableProducer實(shí)現(xiàn)了IHtmlProducer接口且包含了實(shí)現(xiàn)IHtmlProducer接口的對(duì)象,所以它們都是組合模式中的組合對(duì)象。這樣,就能夠通過組合各個(gè)IHtmlProducer非常輕松地生成復(fù)雜的HTML頁(yè)面。而且,添加新的HTML生成器,已有的HTML生成器也絲毫不受影響。這里,使用組合模式構(gòu)造了一個(gè)模塊化非常好的類庫(kù)。
2.按目標(biāo)2進(jìn)行設(shè)計(jì):簡(jiǎn)化HTML的生成
HTML生成框架的第一個(gè)目標(biāo)是使視圖開發(fā)人員不受HTML的細(xì)節(jié)和瀏覽器特有的行為的困擾,到目前為止,已經(jīng)在HTML之上提供了足夠的抽象,即使對(duì)HTML一無所知,用戶界面開發(fā)人員也能順利地生成表格并放到頁(yè)面當(dāng)中。
1)瀏覽器特有的HTML
雖然已經(jīng)支持模塊化的組合構(gòu)造,但是卻沒有提供任何瀏覽器特有的HTML生成功能。如果對(duì)每個(gè)元素,都為每款瀏覽器特有的版本開發(fā)一個(gè)單獨(dú)的類,就非常簡(jiǎn)單了,但卻是相當(dāng)冗長(zhǎng)、乏味的工作。
如果為每種瀏覽器都單獨(dú)生成一個(gè)HTML表格類,那么會(huì)得到類似如圖11-11所示的層次結(jié)構(gòu)。如果存在任何瀏覽器特有的行為,都可以在TableProducer子類中將其覆蓋。就瀏覽器而言,HTML表格已經(jīng)標(biāo)準(zhǔn)化了,一般的TableProducer就能完成絕大部分的工作,但是,有些元素在Netscape中的實(shí)現(xiàn)和IE中的實(shí)現(xiàn)有很大的差別。
實(shí)現(xiàn)類封裝了特定瀏覽器的行為。一旦視圖得到了正確的實(shí)現(xiàn),就可以通過基類定義的接口與之交互。但是,每個(gè)視圖需要知道有哪些實(shí)現(xiàn)可用,以及在某個(gè)特定的環(huán)境下,選用哪一個(gè)最合適。這就意味著,添加任何對(duì)新型瀏覽器的支持都必須在每個(gè)視圖中進(jìn)行修改,這絕對(duì)不能接受。
圖11-11特定瀏覽器的HTML生成器
2)抽象工廠模式
抽象工廠模式提供了將變動(dòng)的實(shí)現(xiàn)隱藏到公共接口之后的途徑。其意圖是:在無需指定具體類的情況下,為生成一簇相關(guān)的或相互依賴的對(duì)象提供接口。下面,看一看抽象工廠模式,然后再看看如何將它應(yīng)用到例子中。
首先,對(duì)某種特定的產(chǎn)品而言,存在一個(gè)所有實(shí)現(xiàn)類共享的公共接口:AbstracProduct。在例子中,TableProducer就是AbstractProduct。對(duì)某個(gè)特定的產(chǎn)品族而言,由ConcreteFactory生成AbstractProduct產(chǎn)品族內(nèi)的產(chǎn)品。例如,對(duì)Netscape和IE來說,可以存在兩個(gè)單獨(dú)的ConcreteFactory類。任何一個(gè)實(shí)現(xiàn),例如TableProducerIE,都是AbstractProduct產(chǎn)品族的ConcreteProduct。
圖11-12顯示了抽象工廠模式應(yīng)用到例子中的情形。
圖11-12特定瀏覽器的HTML生成器的AbstractFactory
NetscapeFactory通過建立Netscape特有的對(duì)象來覆蓋AbstractFactory接口中的方法,Netscape對(duì)象用于擴(kuò)展TableProducer、或PageProducer。當(dāng)客戶請(qǐng)求某個(gè)產(chǎn)品時(shí),AbstractFactory識(shí)別出正確的ConcreteFactory,并要求它生產(chǎn)ConcreteProduct。記住,客戶根本不會(huì)看到ConcreteFactory或ConcreteProduct。表示代碼擁有一個(gè)特殊引用,它指向AbstractFactory的任意實(shí)現(xiàn)。當(dāng)表示代碼請(qǐng)求某個(gè)特定類型的生成器時(shí),ConcreteFactory就生成所請(qǐng)求類型的生成器,但是,它只將生成器的引用返回給此生成器的抽象基類,例如PageProducer或TableProducer。
3)設(shè)計(jì)評(píng)估
使用抽象工廠模式會(huì)有一個(gè)缺點(diǎn):可能存在多個(gè)產(chǎn)品族和多種產(chǎn)品,對(duì)每種這兩者的組合,必須有不同的ConcreteProduct。在例子中,某些HTML元素在所有的瀏覽器上都標(biāo)準(zhǔn)化了,并且在廠商提供的范圍內(nèi)已經(jīng)完全標(biāo)準(zhǔn)化了,這是理想的情況。例如,因?yàn)轫?yè)眉的HTML已經(jīng)標(biāo)準(zhǔn)化了,所以可以生成一個(gè)通用的PageProducer。
4)生成器工廠(原型模式)
現(xiàn)在對(duì)設(shè)計(jì)方案進(jìn)行修訂,以使其中只存在一個(gè)ProducerFactory,由它來負(fù)責(zé)為給定的瀏覽器和生成器類型找到合適的實(shí)現(xiàn),這樣做更高效。
現(xiàn)在面對(duì)最困難的部分:ProducerFactory如何為某個(gè)組合確定最佳的、具體的生成器?圖11-13表明ProducerFactory擁有一個(gè)具體的生成器列表。這樣,就必須向每個(gè)具體生成器提出一系列的問題:“你是某某類型的生成器嗎?”然后:“你支持某某瀏覽器嗎?”如果是,接著問:“你和某某版本有多接近?”基于這些問題的回答,能夠找到最恰當(dāng)?shù)哪莻€(gè)生成器并要求它拷貝自己,這種方法稱為“原型模式”。
圖11-13基于標(biāo)準(zhǔn)的Factory
這里,ProducerFactory不依賴任何具體實(shí)現(xiàn)。它用的是更高明的做法:它擁有一個(gè)對(duì)象列表,列表對(duì)象的每個(gè)類都實(shí)現(xiàn)了IConcreteHtmlProducer接口。
重新考慮一下前面的向頁(yè)面添加表格的例子。如圖11-14所示的順序圖情形。
這里,假設(shè)該HTML是針對(duì)IE瀏覽器的。視圖對(duì)象使用ProducerFactory得到TextProducer、ImageProducer、TableProducer和PageProducer。像以前一樣,文本和圖像生成器加入到表格中,表格又加入到頁(yè)面里。視圖對(duì)象不直接構(gòu)造生成器,而是委派給ProducerFactory來完成。
圖11-14使用ProducerFactory產(chǎn)生類的示例
所有的邏輯都交給ProducerFactory,包括:確定合適的具體的生成器,并返回該具體生成器的一個(gè)實(shí)例。當(dāng)視圖向ProducerFactory請(qǐng)求一個(gè)TextProducer時(shí),ProducerFactory就利用已有的瀏覽器信息來尋找相匹配的對(duì)象。由于在例子中只存在一個(gè)TextProducer,所以這個(gè)決定很簡(jiǎn)單。注意,從視圖的角度來看,返回的對(duì)象是TextProducer,而不是TextProducerGeneric,視圖不需要了解具體的實(shí)現(xiàn)。接下來,視圖請(qǐng)求一個(gè)TableProducer。ProducerFactory知道有兩個(gè)候選對(duì)象,所以它會(huì)詢問是否支持該瀏覽器。因?yàn)橹荒苡幸粋€(gè)候選者回答是,所以就生成一個(gè)新的拷貝并將其返回。視圖就使用PageProducer接口和TableProducer接口來填充視圖。圖11-15表明了這個(gè)過程。
圖11-15尋找合適生成器的過程
5)重新評(píng)估
已經(jīng)實(shí)現(xiàn)了設(shè)計(jì)目標(biāo)了嗎?界面開發(fā)人員能夠在對(duì)HTML一無所知的情況下生成HTML頁(yè)面,因?yàn)閷ふ易钸m合的、具體的生成器的邏輯已經(jīng)封裝在ProducerFactory內(nèi)。當(dāng)前的設(shè)計(jì)方案保持了HTML生成過程的簡(jiǎn)單化,實(shí)現(xiàn)了這一目標(biāo)。
在繼續(xù)之前,需要確認(rèn)一下:目前對(duì)設(shè)計(jì)方案的修改有沒有損害先前已經(jīng)實(shí)現(xiàn)了的目標(biāo)。從用戶界面開發(fā)人員的角度來看,只是簡(jiǎn)單地將生成器直接實(shí)例化變?yōu)閷?duì)ProducerFactory的請(qǐng)求,獲取HTML的流程并沒有改變,在沒有任何損失的情況下實(shí)現(xiàn)了第2個(gè)目標(biāo)。
3.按目標(biāo)3進(jìn)行設(shè)計(jì):支持可選項(xiàng)
為了實(shí)現(xiàn)第3個(gè)目標(biāo)——支持可選項(xiàng),首先需要考察如何獲取可選項(xiàng),以及如何將可選項(xiàng)應(yīng)用到不同的元素上。
1)實(shí)現(xiàn)可選項(xiàng)的不同途徑
有兩種方式來獲取可選項(xiàng)。第一,可以為每種可選項(xiàng)都單獨(dú)設(shè)計(jì)一個(gè)類。表格方面的可選項(xiàng)可以封裝在TablePreference內(nèi),可以在類中定義一系列的方法來設(shè)置顏色、單元格邊框和間距。這樣,每個(gè)元素都會(huì)有一個(gè)對(duì)應(yīng)的可選項(xiàng)類,且每個(gè)可選項(xiàng)類都有對(duì)應(yīng)的訪問方法。例如,框架也許會(huì)使用下面的代碼來得到頁(yè)面的背景顏色:
StringcolorName=pagePreference.getBackgroundColor();
另一種設(shè)計(jì)方案是包含一個(gè)以java.util.Properties對(duì)象為元素的簡(jiǎn)單列表,每個(gè)元素保存“名稱與值”的組合。例如,page.backgroundColor=LightGray將背景色設(shè)置為淡灰。要想獲得可選項(xiàng),框架可以使用下面的代碼來訪問各個(gè)屬性對(duì)象:
StringcolorName=theProperties.getProperty("page.backgroundColor");
2)權(quán)衡
第一種方式利用編譯器來避免鍵入錯(cuò)誤。例如,編譯器會(huì)捕捉到類似pagePreference.getBackgroundColor這樣的錯(cuò)誤。如果以第二種方式實(shí)現(xiàn),會(huì)遇到相同錯(cuò)誤:
StringcolorName=theProperties.getProperty(“page.backgroundColor”);
編譯器運(yùn)行很良好。但是,代碼可能會(huì)返回null,而不是“LightGray”。
第一種實(shí)現(xiàn)方式實(shí)在是太麻煩了,該方案潛在的要求就是實(shí)現(xiàn)數(shù)百個(gè)極其簡(jiǎn)單的可選項(xiàng)訪問方法。因?yàn)槊總€(gè)可選項(xiàng)都有自己的訪問方法,所以載入或編輯可選項(xiàng)對(duì)象將會(huì)非常復(fù)雜。如果使用標(biāo)準(zhǔn)的Properties類,就能夠很輕松地從文件中載入可選項(xiàng)。
如果采用第一種方案,想添加一種新的可選項(xiàng)類型,或在已有的類型中添加一個(gè)新的可選項(xiàng),都必須修改可選項(xiàng)類,即代碼先載入可選項(xiàng)類,對(duì)其進(jìn)行修改后框架代碼再讀取這些可選項(xiàng)。在第二種方式中想要實(shí)現(xiàn)類似的修改只要改動(dòng)可選項(xiàng)數(shù)據(jù),然后在框架代碼中使用該可選項(xiàng)就行了。
在權(quán)衡利弊后,決定采用第二種方式。雖然采用這種方式會(huì)引入一些潛在的錯(cuò)誤,但是它簡(jiǎn)單明了,且可以使用標(biāo)準(zhǔn)的Java處理方法。如圖11-16所示,可以在IHtmlProducer中添加一個(gè)新方法,使用Properties對(duì)象來保存可選項(xiàng)。至于如何正確地使用這些可選項(xiàng)就是每個(gè)接口具體實(shí)現(xiàn)的任務(wù)了,組合對(duì)象應(yīng)該將Properties對(duì)象傳播給它的所有IHtmlProducer對(duì)象。
圖11-16IHtmlProducer接口
注意,在可選項(xiàng)比較簡(jiǎn)單或中等復(fù)雜度的情況下,將可選項(xiàng)保存在屬性文件中能夠很好地工作。但是,如果要保存的可選項(xiàng)非常復(fù)雜:不同類型的生成器的同一個(gè)可選項(xiàng)都有多個(gè)不同的值,屬性就會(huì)變得非常難以閱讀。另一種解決方案就是使用XML文件來保存可選項(xiàng)。
4.按目標(biāo)4進(jìn)行設(shè)計(jì):可擴(kuò)展性和封裝
除了前面的三個(gè)目標(biāo),還必須保證設(shè)計(jì)方案能夠經(jīng)受住時(shí)間的考驗(yàn)。需求將不可避免地發(fā)生變化,一個(gè)有彈性的系統(tǒng)必須能夠適應(yīng)新的需求。
1)封裝
除了公共接口外,框架內(nèi)的任何修改都不允許傳播到依賴于該框架的表示層。視圖包依賴于通用的HtmlProduction包中的通用HTML生成器,但是不依賴于任何實(shí)現(xiàn)。
要想真正地實(shí)現(xiàn)封裝,通用的HTML生成器也不應(yīng)當(dāng)依賴具體實(shí)現(xiàn)。這樣,就能夠添加新的具體實(shí)現(xiàn),或修改已有的具體實(shí)現(xiàn),而不用擔(dān)心會(huì)出現(xiàn)連鎖反應(yīng)。記住,對(duì)—個(gè)給定的用戶和元素,HtmlProduction包中的ProducerFactory必須挑選出正確的具體實(shí)現(xiàn)。那么這是不是就意味著HtmlProduction包依賴具體實(shí)現(xiàn)呢?不是的,因?yàn)槊總€(gè)具體實(shí)現(xiàn)都實(shí)現(xiàn)了IConcreteProducer接口,所以ProducerFactory就能夠使用此接口定義的方法來確定它是否是最適合的,這是多態(tài)的一種典型應(yīng)用。這里,只需要提供一種將具體的生成器注冊(cè)到工廠中的方法即可。為了完成這一點(diǎn),在ProducerFactory中添加一個(gè)方法,如圖11-17所示。注意,addConcreteProducer方法并沒有引入對(duì)任何具體實(shí)現(xiàn)的依賴。惟一的依賴是針對(duì)IConcreteProducer接口的。
圖11-17可以注冊(cè)生成器的ProducerFactory
這里以生成表格為例,來看一看類之間的依賴關(guān)系,如圖11-18所示。
圖11-18類之間的關(guān)系
從圖11-13中,可以發(fā)現(xiàn),每個(gè)具體生成器類都實(shí)現(xiàn)了IConcreteHtmlProducer接口且繼承了一個(gè)抽象生成器類。
2)評(píng)估包之間的依賴關(guān)系
圖11-19表明了HtmlProduction框架包之間的相互依賴關(guān)系。類之間的依賴關(guān)系能夠合并為包之間的依賴關(guān)系。因?yàn)獒槍?duì)IE和Netscape具體實(shí)現(xiàn)的包中的類都實(shí)現(xiàn)了HtmlProduction包中的接口,所以這兩者都依賴HtmlProduction包。因?yàn)榫唧w實(shí)現(xiàn)類使用IConcreteHtmlProducer進(jìn)行注冊(cè),并繼承了通用生成器的IConcreteHtmlPtoducer接口,所以HtmlProduction包不依賴任何具體實(shí)現(xiàn)。
注意,對(duì)通用類和接口的改動(dòng)將要求具體實(shí)現(xiàn)做出相應(yīng)的改變以適應(yīng)這些修改,如果情況反過來卻不需要任何修改,具體實(shí)現(xiàn)能夠生成、銷毀以及修改而不會(huì)對(duì)HtmlProduction包中其他的類和接口有任何影響。因此,實(shí)現(xiàn)了最后一個(gè)目標(biāo):可擴(kuò)展性和封裝。
圖11-19HtmlProduction框架包之間的依賴關(guān)系
11.7.3填充細(xì)節(jié)
現(xiàn)在已經(jīng)完成了高層設(shè)計(jì)并一實(shí)現(xiàn)了設(shè)計(jì)目標(biāo),就可以填充細(xì)節(jié)了,為用戶界面原型中的一些界面完成詳細(xì)的順序圖和類圖。
1.登錄界面
從登錄界面看,很明顯,需要迅速生成一個(gè)非常簡(jiǎn)單的文本輸入表單,這個(gè)表單由兩個(gè)輸入框和一個(gè)提交按鈕組成,每個(gè)輸入框都有一個(gè)標(biāo)簽、名稱和初始的默認(rèn)值,提交按鈕需要一個(gè)標(biāo)簽。圖11-20顯示了一個(gè)Login表單和一個(gè)通用的輸入表單生成器類。
圖11-20Login表單和TabularInputFormProducer類
圖11-21中的順序圖顯示了LoginServlet對(duì)象如何生成此HTML頁(yè)面。LoginServlet對(duì)象從ProducerFactory處獲得TabularInputFormProducer對(duì)象,然后,用正確的提交目標(biāo)和提交標(biāo)記來配置它。接下來,向其中加入用戶名和密碼域,還使用相似的步驟來生成和配置頁(yè)面。TabularInputFormProducer被加入到PageProducer中,并使用可選項(xiàng)對(duì)PageProducer進(jìn)行設(shè)置。
圖11-21為L(zhǎng)ogin表單生成HTML
需要注意的是:視圖需要知道的是少之又少。不需要任何HTML方面的知識(shí),不需要知道瀏覽器的版本或Servlet中的任何細(xì)節(jié)。
盡管如此,仍需要回答幾個(gè)問題:一個(gè)實(shí)際的具體TabularInputiFormProducer是如何完成其工作的?它是獨(dú)立地生成HTML,還是使用內(nèi)部的FormProducer對(duì)象和TableProducer對(duì)象呢?圖11-22表明了TabularInputFormProducer是如何構(gòu)造的。
圖11-22生成一個(gè)TabularInputForm
對(duì)這個(gè)例子而言,假設(shè)具體生成器就是TabularInputFormGeneric。工廠生成一個(gè)原型對(duì)象的拷貝(參見原型模式)。在構(gòu)造拷貝的過程中,一個(gè)表格生成器被添加到表單生成器中。TabularInputFormProducerGeneric包含一個(gè)FormProducer并將一個(gè)TableProducer添加到FormProducer中。需要注意的是,像其他對(duì)象一樣,TabularInputFormProducerGeneric使用相同的方式—從ProducerFactory處獲得具體生成器。
視圖向TabularInputFormProducer發(fā)出了大量的消息,這些消息又發(fā)往其他對(duì)象。當(dāng)視圖添加一個(gè)域時(shí),TabularInputFormProducer就會(huì)向TableProducer添加一個(gè)相應(yīng)的文本標(biāo)簽和文本域。此外,請(qǐng)注意圖11-23中,setPreferences()消息和getHtml()消息是如何從TabularInputFormProducer層層傳遞到FormProducer和TableProducer的(因?yàn)門ableProducer是FormProducer的一部分),接下來,消息從TableProducer繼續(xù)層層傳遞到TextProducer和TextFieldProducer(它們是TableProducer的一部分)。這種消息的層層向下傳遞正是組合模式的特征。
隨著順序圖逐漸復(fù)雜化,使用注釋對(duì)其進(jìn)行說明。例如,某個(gè)方法可能因?yàn)閮蓚€(gè)不同的目的而被調(diào)用,就可以使用注釋將低級(jí)的方法調(diào)用連接起來。在許多情況下,注釋的作用與偽碼非常類似—描述一系列方法調(diào)用的意圖。
圖11-24表明了每個(gè)TabularInputFormProducer對(duì)象是如何與FormProducer對(duì)象以及TableProducer對(duì)象關(guān)聯(lián)的,另外,還強(qiáng)調(diào)了TabularInputFormProducer依賴其他生成器的方式,這里并沒有跟蹤這些生成器。有一點(diǎn)是非常清楚的,就是眾多對(duì)象,包括視圖和TabularInputFormProducer對(duì)象都需要指向ProducerFactory對(duì)象的引用,而且,系統(tǒng)中存在一個(gè)工廠就足夠了。這樣,就可以使用單件模式來實(shí)現(xiàn),向ProducerFactory類添加一個(gè)新的靜態(tài)gerFactorySingleton()方法。
圖11-23添加到TabularInputForm圖11-24參與TabularInputFormProducer的類
評(píng)估:讓TabularInputFormProducer使用TableProducer看起來是合乎邏輯的。但是,這樣做存在一個(gè)缺點(diǎn):普通的表格可能與簡(jiǎn)單的輸入表單大不相同。例如,界面是一個(gè)簡(jiǎn)單的輸入表單,不希望用邊框或不同的背景色吸引對(duì)表格的注意力;而數(shù)據(jù)表格就應(yīng)該清晰地區(qū)分行和列。這里使用表格來得到合理的安排布局,因此可能就需要為內(nèi)部的TableProducer設(shè)置不同的可選項(xiàng),所以,在將可選項(xiàng)傳遞給TableProducer之前,應(yīng)當(dāng)能對(duì)其進(jìn)行修改。
2.工時(shí)條目
下一個(gè)挑戰(zhàn)來自考勤卡表單。正如在圖11-25中所看到的,這個(gè)表單由文本和表格中的文本輸入域組成。
圖11-25考勤卡表單
現(xiàn)在,已經(jīng)有了進(jìn)行外圍封裝的表格和文本生成器,將TextProducer和TextFieldProducer添加到TableProducer不費(fèi)什么力氣。因?yàn)榭蚣苤胁淮嬖谑裁葱碌男袨?,并且在后面?huì)詳細(xì)介紹這種情形下如何使用該框架,所以,這兒就省略了順序圖和類圖。
11.7.4實(shí)現(xiàn)工作流
接下來就是實(shí)現(xiàn)工作流,將實(shí)現(xiàn)設(shè)計(jì)
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝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ù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 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ì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 施工現(xiàn)場(chǎng)卸料風(fēng)險(xiǎn)點(diǎn)告知卡
- 職場(chǎng)技能提升的家庭作業(yè)實(shí)踐案例
- 家庭健康教育從理論到實(shí)踐的探索
- 科技展會(huì)中的人工智能與用戶體驗(yàn)研究報(bào)告
- 二手房銷售合同樣本大全
- 臨時(shí)倉(cāng)儲(chǔ)設(shè)備租賃合同2025
- 二手房買賣合同補(bǔ)充協(xié)議書范本
- 產(chǎn)品銷售獨(dú)家代理合同樣本
- 中介代理辦公租賃合同
- 人事管理外包合同細(xì)則
- 中層領(lǐng)導(dǎo)的高績(jī)效管理
- 小小銀行家-兒童銀行知識(shí)、理財(cái)知識(shí)培訓(xùn)
- 物業(yè)公司縮減人員方案范本
- 機(jī)械基礎(chǔ)知識(shí)競(jìng)賽題庫(kù)附答案(100題)
- 2022年上學(xué)期八年級(jí)期末考試數(shù)學(xué)試卷
- 閱讀理解特訓(xùn)卷-英語四年級(jí)上冊(cè)譯林版三起含答案
- 國(guó)庫(kù)集中支付培訓(xùn)班資料-國(guó)庫(kù)集中支付制度及業(yè)務(wù)操作教學(xué)課件
- 屋面及防水工程施工(第二版)PPT完整全套教學(xué)課件
- 2023年上海青浦區(qū)區(qū)管企業(yè)統(tǒng)一招考聘用筆試題庫(kù)含答案解析
- 2023年高一物理期末考試卷(人教版)
- 2023版押品考試題庫(kù)必考點(diǎn)含答案
評(píng)論
0/150
提交評(píng)論