天天看點

軟體設計模式

設計模式(英語 design pattern)是對面向對象設計中反複出現的問題的解決方案。這個術語是在1990年代由Erich Gamma等人從建築設計領域引入到計算機科學中來的。這個術語的含義還存有争議。算法不是設計模式,因為算法緻力于解決問題而非設計問題。設計模式通常描述了一組互相緊密作用的類與對象。設計模式提供一種讨論軟體設計的公共語言,使得熟練設計者的設計經驗可以被初學者和其他設計者掌握。設計模式還為軟體重構提供了目标。

随着軟體開發社群對設計模式的興趣日益增長,已經出版了一些相關的專著,定期召開相應的研讨會,而且Ward Cunningham為此發明了WikiWiki用來交流設計模式的經驗。

肯特·貝克和沃德·坎甯安在1987年利用克裡斯托佛·亞曆山大在建築設計領域裡的思想開發了設計模式并把此思想應用在Smalltalk中的圖形使用者接口的生成中。一年後Erich Gamma在他的蘇黎世大學博士畢業論文中開始嘗試把這種思想改寫為适用于軟體開發。于此同時James Coplien 在1989年至1991 年也在利用相同的思想緻力于C++的開發,而後于1991年發表了他的著作Advanced C++ Idioms。就在這一年Erich Gamma 得到了博士學位,然後去了美國,在那與Richard Helm, Ralph Johnson ,John Vlissides合作出版了Design Patterns - Elements of Reusable Object-Oriented Software 一書,在此書中共收錄了23個設計模式。這四位作者在軟體開發領域裡也以他們的匿名著稱Gang of Fou簡稱GoF),并且是他們在此書中的協作導緻了軟體設計模式的突破。有時這個匿名GoF也會用于指代前面提到的那本書。

盡管名稱和順序在不同的資料中各有不同,描述模式的格式大緻分為以下四個主要部分:

模式名稱(Pattern Name):每一個模式都有自己的名字,模式的名字使得我們可以讨論我們的設計。

問題(Problem):在面向對象的系統設計過程中反複出現的特定場合,它導緻我們采用某個模式。

解決方案(Solution):上述問題的解決方案,其内容給出了設計的各個組成部分,它們之間的關系、職責劃分和協作方式。

效果(Consequence):采用該模式對軟體系統其他部分的影響,比如對系統的擴充性、可移植性的影響。影響也包括負面的影響。

别名(Also Known As):一個模式可以有超過一個以上的名稱。這些名稱應該要在這一節注明。

動機(Motivation):該模式應該利用在哪種情況下是本節提供的方案(包括問題與來龍去脈)的責任。

應用(Applicability)

結構(Structure):這部分常用類圖與互動圖闡述此模式。

參與者(Participants):這部分提供一份本模式用到的類與物件清單,與它們在設計下扮演的腳色。

合作(Collaboration):描述在此模式下,類與物件間的互動。

結果(Consequences):這部分應描述使用本模式後的結果、副作用、與交換(trade-off)

實作(Implementaion):這部分應描述實作該模式、該模式的部分方案、實作該模式的可能技術、或者建議實作模式的方法。

例程(Sample Code):示範程式。

已知應用(Known Uses):業界已知的實做範例。

相關模式(Related Patterns):這部分包括其他相關模式,以及與其他類似模式的不同。

對某個問題經常出現的、在設計中應該盡量避免的、壞的設計方案被稱為反模式。

基礎模式

委托模式

接口模式

代理模式

抽象工廠模式(Abstract Factory) ,提供一個建立一系列相關或互相依賴對象的接口,而無需指定它們具體的類。

生成器模式 (Builder),将一個複雜對象的建構與它的表示分離,使得同樣的建構過程可以建立不同的表示。

工廠方法模式(Factory Methord) ,定義一個用于建立對象的接口,讓子類決定将哪一個類執行個體化。Factory Method使一個類的執行個體化延遲到其子類。

原型模式 (Prototype) ,用原型執行個體指定建立對象的種類,并且通過拷貝這個原型來建立新的對象。

單例模式(Singleton),保證一個類僅有一個執行個體,并提供一個通路它的全局通路點。

擴充卡模式 (Adapter) ,将一個類的接口轉換成客戶希望的另外一個接口。Adapter模式使得原本由于接口不相容而不能一起工作的那些類可以一起工作。

橋接模式(Bridge) ,将抽象部分與它的實作部分分離,使它們都可以獨立地變化。

組合模式(Composite) ,将對象組合成樹形結構以表示"部分-整體"的層次結構。它使得客戶對單個對象和複合對象的使用具有一緻性。

容器模式

修飾模式 (Decorator) ,動态地給一個對象添加一些額外的職責。就擴充功能而言, 它比生成子類方式更為靈活。

擴充性模式

外觀模式

享元模式

管道與過濾器模式

代理模式(Proxy) ,為其他對象提供一個代理以控制對這個對象的通路。

責任鍊模式 (Chain of Responsibility) ,為解除請求的發送者和接收者之間耦合,而使多個對象都有機會處理這個請求。将這些對象連成一條鍊,并沿着這條鍊傳遞該請求,直到有一個對象處理它。

指令模式 (Command) ,将一個請求封裝為一個對象,進而使你可用不同的請求對客戶進行參數化;對請求排隊或記錄請求日志,以及支援可取消的操作。

柯裡化模式

事件監聽器模式

解釋器模式

疊代器模式

中介者模式

備忘錄模式 (Memento) ,在不破壞封裝性的前提下,捕獲一個對象的内部狀态,并在該對象之外儲存這個狀态。這樣以後就可将該對象恢複到儲存的狀态。

觀察者模式(Observer) ,定義對象間的一種一對多的依賴關系,以便當一個對象的狀态發生改變時,所有依賴于它的對象都得到通知并自動重新整理。

狀态模式 (State) ,允許一個對象在其内部狀态改變時改變它的行為。對象看起來似乎修改了它所屬的類。

政策模式 (Strategy) ,定義一系列的算法,把它們一個個封裝起來, 并且使它們可互相替換。本模式使得算法的變化可獨立于使用它的客戶。

模闆方法模式

通路者模式 (Visitor),表示一個作用于某對象結構中的各元素的操作。它使你可以在不改變各元素的類的前提下定義作用于這些元素的新操作。

層次通路者模式

模式 Action at a distance

模式 Balking

模式 Guarded suspension

模式 Scheduler

模式 Read write lock

模式 Double checked locking

模式 Disable job requests while running job

模式 Scheduled task

模式 User interface

模型-視圖-控制器模式

大家都開始注意設計模式。那麼,到底我們為什麼要用設計模式呢?這麼多設計模式為什麼要這麼設計呢?說實話,以前我還真沒搞清楚。就是看大家一口一個"Design pattern",心就有點發虛。于是就買了本設計模式,結果看得似懂非懂:看得時候好像是懂了,過一會就忘了。可能是本人比較"愚鈍"吧:))最近,有了點感悟。"獨樂不如衆樂",與大家分享一下,還望指教! 為什麼要提倡"Design Pattern"呢?根本原因是為了代碼複用,增加可維護性。那麼怎麼才能實作代碼複用呢?OO界有前輩的幾個原則:"開-閉"原則(Open Closed Principal)、裡氏代換原則、合成複用原則。設計模式就是實作了這些原則,進而達到了代碼複用、增加可維護性的目的。

此原則是由"Bertrand Meyer"提出的。原文是:"Software entities should be open for extension,but closed for modification"。就是說子產品應對擴充開放,而對修改關閉。子產品應盡量在不修改原(是"原",指原來的代碼)代碼的情況下進行擴充。那麼怎麼擴充呢?我們看工廠模式"factory pattern":假設中關村有一個賣盜版盤和毛片的小子,我們給他設計一"CD光牒銷售管理軟體"。我們應該先設計一"CD光牒"接口。如圖:【pre】______________|<>|| CD光牒 ||_____________||+賣() || ||_____________|【/pre】而盜版盤和毛片是其子類。小子通過"DiscFactory"來管理這些CD光牒。代碼為:

有人要買盜版盤,怎麼實作呢?

public class 小子{ public static void main(String【】 args){ CD光牒 d=DiscFactory.getDisc("盜版盤"); CD光牒.賣(); } }

如果有一天,這小子良心發現了,開始賣正版軟體。沒關系,我們隻要再建立一個"CD光牒"的子類"正版軟體"就可以了。不需要修改原結構和代碼。怎麼樣?對擴充開放,對修改關閉。"開-閉原則" 工廠模式是對具體産品進行擴充,有的項目可能需要更多的擴充性,要對這個"工廠"也進行擴充,那就成了"抽象工廠模式"。

裡氏代換原則是由"Barbara Liskov"提出的。如果調用的是父類的話,那麼換成子類也完全可以運作。比如: CD光牒 d=new 盜版盤(); d.賣(); 要将"盜版盤"類改為"毛片"類,沒問題,完全可以運作。Java編譯程式會檢查程式是否符合裡氏代換原則。還記得java繼承的一個原則嗎?子類override方法的通路權限不能小于父類對應方法的通路權限。比如"CD光牒"中的方法"賣"通路權限是"public",那麼"盜版盤"和"毛片"中的"賣"方法就不能是protected或private,編譯不能通過。為什麼要這樣呢?你想啊:如果"盜版盤"的"賣"方法是private。那麼下面這段代碼就不能執行了: CD光牒 d=new 盜版盤(); d.賣();可以說:裡氏代換原則是繼承複用的一個基礎。

就是說要少用繼承,多用合成關系來實作。我曾經這樣寫過程式:有幾個類要與資料庫打交道,就寫了一個資料庫操作的類,然後别的跟資料庫打交道的類都繼承這個。結果後來,我修改了資料庫操作類的一個方法,各個類都需要改動。"牽一發而動全身"!面向對象是要把波動限制在盡量小的範圍。

在Java中,應盡量針對Interface程式設計,而非實作類。這樣,更換子類不會影響調用它方法的代碼。要讓各個類盡可能少的跟别人聯系,"不要與陌生人說話"。這樣,城門失火,才不至于殃及池魚。擴充性和維護性才能提高

了解了這些原則,再看設計模式,隻是在具體問題上怎麼實作這些原則而已。張無忌學太極拳,忘記了所有招式,打倒了"玄幂二老",所謂"心中無招"。設計模式可謂招數,如果先學通了各種模式,又忘掉了所有模式而随心所欲,可謂OO之最高境界。呵呵,搞笑,搞笑!(JR)

依賴倒轉原則抽象不應該依賴與細節,細節應當依

要針對接口程式設計,而不是針對實作程式設計。傳遞參數,或者在組合聚合關系中,盡量引用層次高的類。主要是在構造對象時可以動态的建立各種具體對象,當然如果一些具體類比較穩定,就不必在弄一個抽象類做它的父類,這樣有畫舌添足的感覺接口隔離原則定制服務的例子,每一

一種角色,不多不少,不幹不該幹的事,該幹的事都要幹 抽象類抽象類不會有執行個體

類為子類繼承,一般包含這個系的共同屬性和方法。注意:好的繼承關系中,隻有葉節點是具體類,其他節點應該都是抽象類,也就是說具體類是不被繼承的。将盡可能多的共同代碼放到抽象類中。 7 迪米特法則最少知識原則。不要和陌生人說話。

設計模式使人們可以更加簡單友善地複用成功的設計和體系結構。将已證明的技術表述成設計模式也會使新系統開發者更加容易了解其設計思路。

模式名稱

一個助記名,它用一兩個詞來描述模式的問題、解決方案和效果。命名一個新的模式增加了我們的設計詞彙。設計模式允許我們在較高的抽象層次上進行設計。基于一個模式詞彙表,我們自己以及同僚之間就可以讨論模式并在編寫文檔時使用它們。模式名可以幫助我們思考,便于我們與其他人交流設計思想及設計結果。找到恰當的模式名也是我們設計模式編目工作的難點之一。

問題

描述問題存在的前因後果,它可能描述了特定的設計問題,如怎樣用對象表示算法等。也可能描述了導緻不靈活設計的類或對象結構。有時候,問題部分會包括使用模式必須滿足的一系列先決條件。

解決方案

描述了設計的組成成分,它們之間的互相關系及各自的職責和協作方式。因為模式就像一個模闆,可應用于多種不同場合,是以解決方案并不描述一個特定而具體的設計或實作,而是提供設計問題的抽象描述和怎樣用一個具有一般意義的元素組合(類或對象組合)來解決這個問題。

效果

描述了模式應用的效果及使用模式應權衡的問題。盡管我們描述設計決策時,并不總提到模式效果,但它們對于評價設計選擇和了解使用模式的代價及好處具有重要意義。軟體效果大多關注對時間和空間的衡量,它們也表述了語言和實作問題。因為複用是面向對象設計的要素之一,是以模式效果包括它對系統的靈活性、擴充性或可移植性的影響,顯式地列出這些效果對了解和評價這些模式很有幫助。

繼續閱讀