沒有面向對象的模式設計的軟件工程 (OOSE),就好像沒有菜譜的烹飪一樣。模式使用內容引導我們并且一步一步地對我們進行指導,來集合產生一個復發問題的解決方案。就像我們烹飪時需要菜譜一樣,我們把模式作為可重復的,被證明過的東西來使用,并且使軟件工程變得更加可靠和成功。
在烹飪中,剁和切在調制和調味菜肴中是必備的技術,同樣,在所有種類的挑戰中——包括基本的、中等的以及高級的,根據您的需要會有很多的設計模式。然而,食譜中經常包含一些輔助材料,其使得主菜更加美味,從而增強整個菜肴的質量。
這篇文章將會集中討論這些模式之間的關系、組合以及變化。這就是我們常稱的“基于模式的軟件工程”。我提供的例子將會以形象的UML來顯示,并且最終會被轉化成為代碼形式(例如Java語言)。由于模式不只是影響類和對象的結構,還有動態(操作),所以這篇文章將會對在面向服務的體系架構(SOA)中模式所承擔任務的調研得出結論。
模式的概念
模式在軟件工程中的崛起,使得我們開始注意到復現問題。如果你設計一個軟件,遇到一個情形時你問自己:“Gee,我不是第一個面對這個問題的人!”你對于一個模式的研究就已經開始了。一旦你找到并使用一個模式,你的解決方案將不僅受益于從過去獲得的知識,而且這個模式還為相關的模式打開了一扇門。一個單獨的模式為它所描述的內容工作,并且提供更多的用來改進你的解決方案質量的各種相關的模式。最終,在一個設計中,模式可以成為一個整個基于模式的設計過程的開始點。
在我們討論模式之間的關系之前,讓我們再探究一下剛才提到的烹飪的比喻,來看一看一些單個的模式。
我將會描述一個典型的電視烹飪節目,來幫助我解釋軟件模式和他們之間的關系。電視節目的目的是演示一個菜肴的準備過程。在大多數的烹飪節目中,我們會在廚師前面找到杯子和碗,例如準備好的洋蔥放在杯子里。這是因為專業的廚師不需要在電視觀眾面前演示如何切洋蔥;這太令人感到無聊了。在錄制電視節目之前,廚師已經讓他的幫廚“切好了適量的洋蔥”。這里的重點是,廚師不需要交流切菜的技術,而只需要告訴大家結果:一杯切好的洋蔥。
軟件工程師也使用這種基本的模式。這些模式中,例如General Responsbility Assignment Software Patterns (GRASP), 1 是最基本的模式,很多其他的模式都要用到它們?;镜脑O計模式組織并控制通訊或創建,或者他們在對象之間建立可視性?;旧?,在一個面向對象的系統中,對象通過消息進行互相的交流。因此,所有這些消息(或者稱作職責)需要被軟件工程師分配,用來建立一個靈活的、可維護的系統。在這個事實的基礎上,面向對象的軟件工程師不斷地問自己同樣的一個基本問題:“誰應該和誰說話?”。
問題場景
在這篇文章的下面部分,我將會舉例說明在一個時間表應用程序(根據時間表歸屬而變化)不斷變化的場景中,使用各種模式的方法。圖1顯示了一個典型的面向對象設計者的情形,這是一個特殊的商業規定,它要求辨別時間表是否被分配。問題(“時間表被分配了么?”),回答(“是的”或者“沒有”)需要被確定,但是問題仍然存在:誰來接受以及誰來發送這些消息?
圖1:職責分配
甚至對于非?;A的設計情形,例如圖1所描述的,我們也可以使用基本的設計模式;例如,GRASP模式。
在電視烹飪節目中,廚師使用了一個基本的模式——切好的洋蔥——來匯集一個他自己的更加復雜的模式,他所做的菜肴。模式的級別已經從一系列單獨的方面提高到了一整盤菜中,他包含不同的基本技術。食譜有一個名稱;例如番茄醬。廚師的職責是決定使用和準備多少的洋蔥。問題被移到一個更高的層次:從切洋蔥到做出一份美味的番茄醬。廚師開始使用他自己的模式——食譜,其中包含很多其他的模式(例如炒,切芹菜等等)。經驗豐富的廚師會在特定的情況使用特定的模式,來表現出菜肴色澤,紋理和模式的精細。
軟件設計模式是不同的,除了基本的GRASP模式外,工程師還會使用更多的被提升的模式,例如Gang of Four 2 (GoF)或者架構模式?,F在大部分從大學畢業的軟件工程師都是基于面向對象的原理,軟件開發行業已經開始提升模式的層級,從問題解決技術層級提升到問題預防技術層級。我將會使用設計模式——可重用對象 (來自Gang of Four)作為一個設計模式來演示模式之間的關系,并且使用IBM Rational Software Architect (RSA) 模式舉例說明。
讓我們回到最初的場景,請看圖1,我們打算基于分配過程建立一個時間表應用程序。設計人員需要分辨時間表是否被分配。在這個場景中,只需要填加一個叫做isApproved的屬性到Timesheet對象,它包含一個布爾型的值true或者false。這個解決方法的問題是,對象的屬性可能會改變,依靠屬性的內容,我們需要決定將要發送的消息的類型。如果我們想要添加另外一個選項——例如,Submitted——布爾型屬性,它擁有兩個值true或者false,就不再需要提供這個設計方法了。由于介紹了Submitted聲明,最初的設計(為兩個值建立的)將會暫停并且整個業務邏輯需要我們再次評估最初的設計。
稍后我將會演示當模式被使用時,從一個兩個狀態設計到三個狀態設計的轉換是多么的平滑。如圖2所示,我們的新設計方法將會違反兩個基本的設計模式,Expert和Polymorphism, 3 并且沒有必要將一個對象和另一個對象的業務邏輯結合在一起。
布爾值方法將不僅會違反基本的設計模式,同時由于Timesheet對象可以輕松的暫停,并且整個對象需要根據每一個變化而重新測試,因此它還會增加軟件工程師的維護負擔。
圖2:一個違反Expert和Polymorphism模式的UML圖
把圖2轉換成UML設計將會產生一個類似于圖3的Java結構。
圖3:一個Java的例子,違反Expert和Polymorphism模式
一個解決方案:State 模式
GoF模式目錄為我們的設計挑戰提供了一個可能的解決方案。這個模式叫做State。
首先我們需要驗證這個模式是否滿足我們的需求,并且閱讀模式的目的,應用和結論部分。因為模式要求它“允許一個對象在它的內部狀態變化的時候改變它的行為。對象將會出現改變它的類[GoF]”,我們繼續并應用這個模式到我們的問題中。
使用State這個模式的一個好處是,它可以通過孤立各種狀態來分解圖3中if語句的情景。UML狀態機符號幫助我們描述并研究了各種狀態。最初我們的時間表非常簡單,并且我們從存在的結構中隔離了兩個狀態,Approved和NotApproved。
圖4:時間表的UML狀態機圖表(兩個狀態)
取代詢問對象的哪一個值嵌套在一個屬性中(我們這里是isApproved)以及根據這個做決定(違反polymorphism的原則),我們現在可以告訴對象去做什么,簡單的發送消息給它并且讓Timesheet對象來處理事件。我們想要設計的是發送消息的某種方式,如下圖所示,ts是一個Timesheet對象。
圖5:時間表的新職責分配(Java)
在我們隔離各種狀態之后,從Timesheet對象中移除if結構,然后分配三個職責(開始,批準和拒絕),我們想要應用State模式到我們的解決方案中。使用RSA模式瀏覽器導航到State模式,它顯示了我們的模式中參與的類。
圖6:State模式和RSA模式瀏覽器中參與的類
為了獲得State模式的整體構架視圖,模式瀏覽器提供給我們下列布局圖:
圖7:RSA模式瀏覽器中的State模式構架
State模式的cookie-cutter解決方案需要被調整成為適應我們的應用程序細節的需求。在從模式瀏覽器直接拖拽模式到我們的工作臺之后,我們可以從應用程序細節類模塊中分配參加的類。下列的圖表包含一個作為內容對象的Timsheet模式,State的Java接口ITimesheetState以及來自我們時間表應用程序(Approved和NotApproved)的兩個具體狀態。
圖8:RSA中應用的狀態模式
這個模式的Java預覽圖如圖9所示。在消息approve()被發送到Timesheet對象之后,它獲得消息并且委派它到它的狀態,并提供一個回指自身的指示器(this參數)。
圖9:從內容到狀態對象的消息委托
在approve(this)消息被發送之后,處于運行時間的狀態被定位于State對象,它將會處理事件(它是真實的多態)。例如,NotApproved狀態將會執行approve(ITimesheetState state)消息,如下所示:
圖10:固化狀態方法實現——未批準
為了支持多態方法,我們需要指派分配職責,也就是批準狀態,即使在這個特定情形中我們什么也不需要做。
圖11:固化狀態方法實現——已批準
現在State模式[GoF]已經被分配了,讓我們看看在我們的模式設計中發生了什么,是否發生了一個需求的變化:例如,投資人需要在時間輸入并且請求確認之后能夠遞交他們的時間表。下列的狀態機圖表顯示了兩個新的狀態,開始和提交,他們代替了之前的未批準狀態,來適應這個需求變化。
圖12:時間表的UML狀態機(多種狀態)
圖13中的UML Design Class圖表描述了由新的需求引起的類模塊的變化。即使新狀態類和消息已經被提出并且一個狀態已經被移除,變化仍然是易于管理的。最重要的一點是Timesheet并沒有被改變。它仍然持續傳送它所收到的所有信息到它的實際狀態。這對于我們圖3中的if-else結構是一個巨大的改進,因為在測試視圖中所涉及的領域,已經從Timesheet對象轉移到了它的狀態中。
圖13:部分UML設計類圖表(時間表和新狀態)
基于模式的開發
之前,我闡述了一個設計難題,并對其應用了一個普通解決方案。(State 模式), 同時指出使用模式(可維護性和彈性)的優勢所在。 在基于模式的解決過程中,設計者不僅在出現問題時要使用模式,還要使用模式去驅動全部的設計。這種方法有稍些不同,因為它以設計者正積極與模式目錄互動并使用模式間的關系作為前提的。通常通過選好的樣板為帶有目錄的模式分組。GoF模式模板,例如,含有 命名和分類,目的,別稱,動機,適用性,結構,參與者,協作,結論,實施,范例代碼,已知使用 ,最后至少還要有,相關模式。
在模式模板中的相關模式部分中含有可以在項目中使用的其它模式的重要線索。例如,通過模式目錄, State 模式經常涉及到Flyweight 和 Singleton[GoF]。這個信息必定會對軟件引擎引發一些列新的問題——例如, “存在既可以從Flyweight 也可從 Singleton中獲益的解決方案么?” ——并且可以使引擎檢查存在的方法。
使用State模式的解決方案有個缺點。假如我們的時間表系統處于操作狀態,例如在已批準狀態下分配5000個時間表,這時我們就擁有5000個已批準狀態實例。同樣,當每個狀態改變時,我們將為新狀態創建一個新的實例并且Java垃圾收集器將收集舊的狀態對象。這對于時間表應用程序也許不是十分關鍵的,但在其他情況下,這將花費很大的資源。圖14僅僅顯示了少數時間表和關聯狀態,它們可以在應用程序中增長成千上萬倍。
圖14:在Flyweight模式之前的對象模型
在時間表例子中,“被證實的,提交的,和進入的”狀態是 Flyweight對象的優秀候選者,因為這里不需要為其中一個狀態增加附加的屬性以便區分這些實例。 使用Flyweight 模式,我們可以進一步改進狀態模式解決方法,如圖15所示。
圖15:使用Flyweight后的對象模型
我們的時間表在任何時刻僅僅包含3個不同狀態實例,它們增強了可維護性和性能。
但是...... 應用Flyweight [GoF] 模式給設計者帶來了新的挑戰。取代了生成一個新的特殊狀態對象實例或者把flyweight對象作為參量,我們要在這個特殊情況下實現這個狀態對象。Singleton [GoF] 模式正好可以完成這個意圖。我們既可以執行每個狀態作為Singleton,也可以創建一個狀態工廠用于生成和管理狀態。后面的方法將進一步通過分離關系來提高可維護性。
在基于模式的開發中,目的地也許就是新的模式的起源。例如,狀態模式經常與Flyweight 和 Singleton 模式相互協調。 然而, Flyweight 和 Singleton與其他模式聯系的更加緊密,等等。簡而言之,我把對這些模式之間的闡述限制在GoF 目錄;模式模板的 Also Known As部分也為其它的目錄敞開了大門。
在面向服務的體系架構中的基于模式開發
在 SOA 中,包含了服務,服務提供者和消費者,就如同這里存在責任,接受者和發送人。不同的是,應用軟件設計中心方法被抬高到服務的高度,它對準了擁有商業步驟的IT系統。迄今為止,我們對時間表應用程序的設計已經由技術和變化的需求所驅動。為了在SOA中包含時間表應用程序,服務必須顯露,這樣人工和非人工界面將會消失。
在SOA設計中,時間表應用程序這個獨立的觀點將被時間表應用程序如何與其他應用程序的服務協調的觀點所取代。依靠商業需求,時間表應用程序可以看成組織的薪水冊程序或者與成本細目分類結構結合用于產品管理。對于企業和應用程序的設計師來說展示或修改已存在的服務以及生成新的服務成了關鍵的任務。因此利用模式建立一個更靈活和可維持的系統成為SOA成功的關鍵因素。
在本文的前面,我展示了模式是如何促進附加相關模式的應用程序,甚至穿過目錄。我們也看到了模式以不同的形式存在,從幫助設計對象層的決定到幫助設計應用層的模式(也就是,分配職責與GoF模式)?;谀J降姆椒ㄔ赟OA中幫助提供靈活的以及可維持的服務,并且SOA自身可以驅動并且激勵基于模式的方法。商業建模模式以及通過SOA激發的體系架構/網絡模式可以自上而下地驅動模式/驅動開發,反之,基于模式的應用程序設計準備了一個成功的自下而上的SOA 。
結論
找到一個和問題匹配的模式不僅僅是引進了一個接解決問題的方法,而且還意味著一個新的研究和對有關模式的更深入的評估。如果你使用過因特網的搜索引擎去搜索一個你不太清楚的話題,此研究/發現/探索的行為方式你應當很熟悉。你經常從一個你不確定的術語出發,但是隨著你看見更多的已接受術語和在你的結果集中未打開的知識領域時,你可以深入了解模式的思想和解決方案。不久,你就可以增強你自己的問題,最終擴展你的原始想法。
當正確地進行編目之后,模式提供了一個公共的路標,鼓勵工程師調查有問題的地方,更重要的是,它讓我們應用一套行之有效的方法而不是單單一種方法。在上文中,我使用了一個基本的增進請求,闡述了改變對處于維護模式下的系統的基于模式的解決方法的影響。隨后,反復—漸進過程模型,在創建過程中項目通常面對非常相似的情形,并且,我們可以很容易的看到利用基于模式的開發對改善工程所帶來的好處。
Rational Software Architect (RSA) 通過普通設計模式(例如GoF)或創建自身的模式目錄來支持基于模式的工程過程。在企業級,發布RSA的模式目錄以及共享模式庫可以提高通過率,可以使得IT設計更加可靠與靈活。反映和適應組織變革的能力是SOA的基本策略。
原文轉自:http://www.anti-gravitydesign.com