




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