1. 程式人生 > >UML類圖概述、設計模式

UML類圖概述、設計模式

占用 對象的訪問 關聯關系 參數類型 復雜度 可用 局部變量 工作 做出


深入淺出UML類圖(http://blog.csdn.net/lovelion/article/details/7843308)

類(Class)封裝了數據和行為,是面向對象的重要組成部分,它是具有相同屬性、操作、關系的對象集合的總稱。
在系統中,每個類都具有一定的職責,職責指的是類要完成什麽樣的功能,要承擔什麽樣的義務。一個類可以有多種職責,設計得好的類一般只有一種職責。在定義類的時候,將類的職責分解為類的屬性和操作(即方法)。類的屬性即類的數據職責,類的操作即類的行為職責。設計類是面向對象設計中最重要的組成部分,也是最復雜和最耗時的部分。

類圖(Class Diagram)使用出現在系統中的不同類來描述系統的靜態結構,它用來描述不同的類以及它們之間的關系。

在系統分析和設計階段,類通常可以分為三種,分別是實體類(Entity Class)、控制類(Control Class)和邊界類(Boundary Class)

,下面對著三種類加以簡要說明:
(1)實體類:實體類對應系統需求中的每個實體,它們通常需要保存在永久存儲體中,一般使用數據庫表和文件來記錄,實體類既包括存儲和傳遞的數據的類,還包括操作數據的類。實體類來源於需求說明中的名詞,如學生、商品等。
(2)控制類:控制類用於體現應用程序的執行邏輯,提供相應的業務操作,將控制類抽象出來可以降低界面和數據庫之間的耦合度。控制類一般是由動賓結構的短語(動詞+名詞)轉化來的名詞,如增加商品對應有一個商品增加類,註冊對應有一個用戶註冊類等。
(3)邊界類:邊界類用於對外部用戶與系統之間的交互對象進行抽象。主要包括界面類,如對話框、窗口、菜單等。

關聯關系
關聯(Association)關系是類與類之間最常用的一種關系,它是一種結構化關系,用於表示一類對象與另一類對象之間有聯系,如汽車和輪胎、師傅和徒弟、班級和學生等等。
在UML圖中,用實線連接有關聯關系的對象所對應的類。
在使用Java、C#和C++等編程語言實現關聯關系時,通常將一個類的對象作為另一個類的成員變量。
在UML中,關聯關系通常又包含如下幾種形式:
(1)雙向關聯

默認情況下,關聯是雙向的。例如:顧客(Customer)購買商品(Product)並擁有商品,反之,賣出的商品總有某個顧客與之相關聯。因此,Customer類和Product類之間具有雙向關聯關系
(2)單向關聯
類的關聯關系也可以是單向的,單向關聯用帶箭頭的實線表示。例如:顧客(Customer)擁有地址(Address),則Customer類與Address類具有單向關聯關系。
(3)自關聯
在系統中可能會存在一些類的屬性對象類型為該類本身,這種特殊的關聯關系稱為自關聯。例如:一個節點類(Node)的成員又是節點Node類型的對象。
(4)多重性關聯
多重性關聯關系又稱為重數性(Multiplicity)關聯關系,表示兩個關聯對象在數量上的對應關系。
在UML中,對象之間的多重性可以直接在關聯直線上用一個數字或一個數字範圍表示
以下是多重性表示方式列表
表示方式 多重性說明
1..1 表示另一個類的一個對象只與該類的一個對象有關系
0..* 表示另一個類的一個對象與該類的零個或多個對象有關系
1..* 表示另一個類的一個對象與該類的一個或多個對象有關系
0..1 表示另一個類的一個對象沒有或只與該類的一個對象有關系
m..n 表示另一個類的一個對象與該類最少m,最多n個對象有關系(m<=n)

聚合關系

聚合(Aggregation)關系表示整體與部分的關系。
在聚合關系中,成員對象是整體對象的一部分,但是成員對象可以脫離整體對象獨立存在。
在UML中,聚合關系用帶空心菱形的直線表示。
例如:汽車發動起引擎(Engine)是汽車(Car)的組成部分,但是汽車發動機可以獨立存在,因此,汽車和發動機是聚合關系。
在代碼實現聚合關系時,成員對象通常作為構造方法、Setter方法或業務方法的參數註入到整體對象中。

組合關系
組合(Composition)關系也表示類之間整體和部分的關系,但是在組合關系中,整體對象可以控制成員對象的生命周期,一旦整體對象不存在,成員對象也將不存在,成員對象與整體對象之間具有同生共死的關系。
在UML中,組合關系用帶實心菱形的直線表示。
例如:人的頭(Head)與嘴巴(Mouth),嘴巴是頭的組成部分之一,而且如果頭沒了,嘴巴也就沒了,因此頭和嘴巴是組合關系。

依賴關系
依賴(Dependency)關系是一種使用關系,特定事物的改變有可能會影響到使用該事物的其他事物,在需要表示一個事物使用另一個事物時使用依賴關系。
大多數情況下,依賴關系體現在某個類的方法使用另一個類的對象作為參數。
在UML中,依賴關系用帶箭頭的虛線表示,由依賴的一方指向被依賴的一方。

是系統實施階段,依賴關系通常通過三種方式來實現。
第一種也是最常用的一種方式是將一個類的對象作為另一個類中方法的參數
第二種方式是在一個類的方法中將另一個類的對象作為其局部變量
第三種方式是在一個類的方法中調用另一個類的靜態方法

泛化關系
泛化(Generalization)關系也就是繼承關系,用於描述父類與子類之間的關系,父類又稱作基類或超類,子類又稱作派生類。
在UML中,泛化關系用帶空心三角形的直線來表示。

實現關系
接口和類之間存在一種實現(Realization)關系
在這種關系中,類實現了接口,類中的操作實現了接口中所聲明的操作。
在UML中,類與接口之間的實現關系用帶空心三角形的虛線來表示

面向對象的編程,並不是類越多越好,類的劃分是為了封裝,但分類的基礎是抽象,具有相同屬性和功能的對象的抽象集合才是類。

關於策略模式
策略模式是一種定義一系列算法的方法,從概念上來看,所有這些算法完成的都是相同的工作,只是實現不同,它可以以相同的方式調用所有的算法,減少了各種算法類與使用算法類之間的耦合。
策略模式的Strategy類層次為Context定義了一系列的可供重用的算法或行為。繼承有助於析取出這些算法中的公共功能。
策略模式的優點是簡化了單元測試,因為每個算法都有自己的類,可以通過自己的接口單獨測試。
當不同的行為堆砌在一個類中時,就很難避免使用條件語句來選擇合適的行為。將這些行為封裝在一個個獨立的Strategy類中,可以在使用這些行為的類中消除條件語句。
策略模式是用來封裝算法的,但在實踐中,我們發現可以用它來封裝幾乎任何類型的規則,只要在分析過程中聽到需要在不同時間應用不同的業務規則,就可以考慮使用策略模式處理這種變化的可能性。
但我感覺在基本的策略模式中,選擇所用具體實現的職責由客戶端對象承擔,並轉給策略模式的Context對象。這本身並沒有解除客戶端需要選擇判斷的壓力,而策略模式與簡單工廠模式結合後,選擇具體實現的職責也可以由Context來承擔,這就最大化地減輕了客戶端的職責。

單一職責原則
就一個類而言,應該僅有一個引起它變化的原因。
如果一個類承擔的職責過多,就等於把這些職責耦合在一起,一個職責的變化可能會削弱或者抑制這個類完成其他職責的能力。這種耦合會導致脆弱的設計,當變化發生時,設計會遭受到意想不到的破壞。
軟件設計真正要做的許多內容,就是發現職責並把那些職責相互分離。
如果你能夠想到多於一個的動機去改變一個類,那麽這個類就具有多於一個的職責,就應該考慮類的職責分離。

開放-封閉原則
開放封閉原則,是說軟件實體(類、模塊、函數等等)應該可以擴展,但是不可修改。
對於擴展是開放的,對於更改是封閉的。
怎樣的設計才能面對需求的改變卻可以保持相對穩定,從而使得系統可以在第一個版本以後不斷推出新的版本呢?開放封閉原則給我們答案。
無論模塊多麽的‘封閉’,都會存在一些無法對之封閉的變化。既然不可能完全封閉,設計人員必須對於他設計的模塊應該對哪種變化封閉做出選擇。他必須先猜測出最有可能發生的變化種類,然後構造抽象來隔離那些變化。
等到發生變化時立即采取行動。
在我們最初編寫代碼時,假設變化不會發生。當變化發生時,我們就創建抽象來隔離以後發生的同類變化。
面對需求,對程序的改動是通過增加新代碼進行的,而不是更改現有的代碼。這是開放封閉原則的精神所在。
我們希望的是在開發工作展開不久就知道可能發生的變化。查明可能發生的變化所等待的時間越長,要創建正確的抽象就越困難。
開放-封閉原則是面向對象設計的核心所在。遵循這個原則可以帶來面向對象技術所聲稱的巨大好處,也就是可維護、可擴展、可重用、靈活性好。開發人員應該僅對程序中呈現出頻繁變化的那些部分做出抽象,然而,對於應用程序中的每個部分都刻意地進行抽象同樣不是一個好主意。拒絕不成熟的抽象和抽象本身一樣重要。

依賴倒轉原則
高層模塊不應該依賴低層模塊。兩個都應該依賴抽象
抽象不應該依賴細節,細節應該依賴於抽象
說白了,就是針對接口編程,不要對實現編程

裏氏替換原則
一個軟件實體如果使用的是一個父類的話,那麽一定適用於其子類,而且它察覺不出父類對象和子類對象的區別。也就是說,在軟件裏面,把父類都替換成它的子類,程序行為沒有變化。
定義:子類型必須能夠替換掉它們的父類型。
只有當子類可以替換掉父類,軟件單位的功能不受到影響時,父類才能真正被復用,而子類也能夠在父類的基礎上增加新的行為
由於子類型的可替換性才使得使用父類類型的模塊在無需修改的情況下就可擴展。
高層模塊不應該依賴低層模塊,兩個都應該依賴抽象。
依賴倒轉其實可以說是面向對象設計的標誌,用哪種語言來編寫程序不重要,如果編寫時考慮的都是如何針對抽象編程而不是針對細節編程,即程序中所有的依賴關系都是終止於抽象類或者接口,那就是面向對象的設計,反之那就是過程化的設計了。

裝飾模式
裝飾模式是為已有功能動態地添加更多功能的一種方式。
當系統需要新功能的時候,是向舊的類中添加新的代碼。這些新加的代碼通常裝飾了原有類的核心職責或主要行為。
在主類中加入了新的字段,新的方法和新的邏輯,從而增加了主類的復雜度。而這些新加入的東西僅僅是為了滿足一些只在某種特定情況下才會執行的特殊行為的需要。而裝飾模式卻提供了一個非常好的解決方案,它把每個要裝飾的功能放在單獨的類中,並讓這個類包裝它所要裝飾的對象,因此,當需要執行特殊行為時,客戶代碼就可以在運行時根據需要有選擇地、按順序地使用裝飾功能包裝對象了。
裝飾模式的優點:把類中的裝飾功能從類中搬移去除,這樣可以簡化原有的類。
有效地把類的核心職責和裝飾功能區分開了。而且可以去除相關類中重復的裝飾邏輯。

代理模式
定義:為其他對象提供一種代理以控制對這個對象的訪問。
代理模式分幾種
第一,遠程代理,也就是為一個對象在不同的地址空間提供局部代表。這樣可以隱藏一個對象存在於不同地址空間的事實。
第二,虛擬代理,是根據需要創建開銷很大的對象。通過它來存放實例化需要很長時間的真實對象。
第三,安全代理,用來控制真實對象訪問時的權限。
第四,智能指引,是指當調用真實的對象時,代理處理另外一些事。

工廠方法模式
簡單工廠模式的最大優點在於工廠類中包含了必要的邏輯判斷,根據客戶端的選擇條件動態實例化相關的類,去除了與具體產品的依賴。
但是簡單工廠模式,違背了開放封閉原則。
工廠方法模式:定義一個用於創建對象的接口,讓子類決定實例化哪一個類。工廠方法使一個類的實例化延遲到其子類。
工廠方法實現時,客戶端需要決定實例化哪一個工廠來實現運算類,選擇判斷的問題還是存在的,也就是說,工廠方法把簡單工廠的內部邏輯判斷移到了客戶端代碼來進行。你想要加功能,本來是改工廠類的,而現在是修改客戶端。

原型模式
原型模式其實就是從一個對象再創建另外一個可定制的對象,而且不需要知道任何創建的細節。
.NET在System命名空間中提供了ICloneable接口,其中只有唯一的一個方法Clone()
一般在初始化的信息不發生變化的情況下,克隆是最好的方法。這既隱藏了對象創建的細節,又對性能是大大的提高。
MemberwiseClone()方法是這樣,如果字段是值類型,則對該字段執行逐位復制,如果字段類型是引用類型,則復制引用但不復制引用的對象:因此,原始對象及其副本引用同一對象。
淺復制:被復制對象的所有變量都含有與原來的對象相同的值,而所有的對其他對象的引用都仍然指向原來的對象。
深復制:把引用對象的變量指向復制過的新對象,而不是原有的被引用的對象

模板方法模式
既然用了繼承,並且肯定這個繼承有意義,就應該要成為子類的模板,所有重復的代碼都應該要上升到父類上去,而不是讓每個子類都去重復。
當我們要完成在某一細節層次一致的一個過程或一系列步驟,但其個別步驟在更詳細的層次上的實現可能不同時,我們通常考慮用模板方法模式來處理。

模板方法模式是通過把不變行為搬到超類,去除子類中的重復代碼來體現它的優勢。
模板方法模式就是提供了一個很好的代碼復用平臺。
當不變的和可變的行為在方法的子類實現中混合在一起的時候,不變的行為就會在子類中重復出現。通過模板方法模式把這些行為搬到單一的地方,這樣就幫助子類擺脫重復的不變行為的糾纏。

模板方法模式定義:定義一個操作中的算法的骨架,而將一些步驟延遲到子類中。模板方法使得子類可以不改變一個算法的結構即可重定義該算法的某些特定步驟。

迪米特法則
也叫最少知識原則
定義:如果兩個類不必彼此直接通信,那麽這兩個類就不應當發生直接的相互作用。如果其中一個類需要調用另一個類的某一個方法的話,可以通過第三者轉發這個調用。
在類的結構設計上,每一個類都應當盡量降低成員的訪問權限
迪米特法則其根本思想,是強調了類之間的松耦合。
類之間的耦合越弱,越有利於復用,一個處在弱耦合的類被修改,不會對有關系的類造成波及。

外觀模式
定義:為子系統中的一組接口提供一個一致的界面,此模式定義了一個高層接口,這個接口使得這一子系統更加容易使用。
首先,在設計階段初期,應該要有意識的將不同的兩個層分離,比如經典的三層架構,就需要考慮在數據訪問層和業務邏輯層、業務邏輯層和表示層的層與層之間建立外觀Facade,這樣可以為復雜的子系統提供一個簡單的接口,使得耦合大大降低。
其次,在開發階段,子系統往往因為不斷的重構演化而變得越來越復雜,增加外觀Facade可以提供一個簡單的接口,減少它們之間的依賴。
第三,在維護一個遺留的大型系統時,可能這個系統已經非常難以維護和擴展,但是因為它包含非常重要的功能,新的需求開發必須要依賴於它。此時用外觀模式Facade也是非常合適的。你可以為新系統開發一個外觀Facade類,來提供設計粗糙或高度復雜的遺留代碼的比較清晰簡單的接口,讓新系統與Facade對象交互,Facade與遺留代碼交互所有復雜的工作。

建造者模式
將一個復雜對象的構建與它的表示分離,使得同樣的構建過程可以創建不同的表示的意圖時,我們需要應用一個設計模式“建造者模式”,又叫生成器模式
如果我們用了建造者模式,那麽用戶就只需要指定需要建造的類型就可以得到它們,而具體建造的過程和細節就不需要知道了。
定義:將一個復雜對象的構建與它的表示分離,使得同樣的構建過程可以創建不同的表示。
此模式主要是用於創建一些復雜的對象,這些對象內部構建間的建造順序通常是穩定的,但對象內部構建通常面臨著復雜的變化。
建造者模式的好處就是使得建造代碼與表示代碼分離,由於建造者隱藏了該產品是如何組裝的,所以若需要改變一個產品的內部表示,只需要再定義一個具體的建造者就可以了。

觀察者模式
觀察者模式定義了一種一對多的依賴關系,讓多個觀察者對象同時監聽某一個主題對象。這個主題對象在狀態發生變化時,會通知所有觀察者對象,使它們能夠自動更新自己。
將一個系統分割成一系列相互協作的類有一個很不好的副作用,那就是需要維護相關對象間的一致性。我們不希望為了維持一致性而使各類緊密耦合,這樣會給維護、擴展和重用都帶來不便。
當一個對象的改變需要同時改變其他對象的時候用此模式。
補充一點,而且它不知道具體有多少對象有待改變時,應該考慮使用觀察者模式。
當一個抽象模型有兩個方面,其中一個方面依賴於另一個方面,這時用觀察者模式可以將這兩者封裝在獨立的對象中使它們各自獨立地改變和復用。
觀察者模式所做的工作其實就是解除耦合。讓耦合的雙方都依賴於抽象,而不是依賴於具體。從而使得各自的變化都不會影響另一邊的變化。
委托就是一種引用方法的類型。一旦為委托分配了方法,委托將與該方法具有完全相同的行為。委托方法的使用可以像其他任何方法一樣,具有參數和返回值。
委托可以看作是對函數的抽象,是函數的‘類’,委托的實例將代表一個具體的函數。
一個委托可以搭載多個方法,所有方法被依次喚起。更重要的是,它可以使得委托對象所搭載的方法並不需要屬於同一個類。
委托對象所搭載的所有方法必須具有相同的原形和形式,也就是擁有相同的參數列表和返回值類型。

抽象工廠模式
定義:提供一個創建一系列相關或相互依賴對象的接口,而無需指定它們具體的類。
抽象工廠模式的優點與缺點:
最大的好處是易於交換產品系列,由於具體工廠類,在一個應用中只需要在初始化的時候出現一次,這就使得改變一個應用的具體工廠變得非常容易,它只需要改變具體工廠即可使用不同的產品配置。
第二大好處是,它讓具體的創建實例過程與客戶端分離,客戶端是通過它們的抽象接口操縱實例,產品的具體類名也被具體工廠的實現分離,不會出現在客戶代碼中。
所有在用簡單工廠的地方,都可以考慮用反射技術來去除switch或if,解除分支判斷帶來的耦合。

狀態模式
面向對象設計其實就是希望做到代碼的責任分解
定義:當一個對象的內在狀態改變時允許改變其行為,這個對象看起來像是改變了其類。
狀態模式主要解決的是當控制一個對象狀態轉換的條件表達式過於復雜時的情況。把狀態的判斷邏輯轉移到表示不同狀態的一系列類當中,可以把復雜的判斷邏輯簡化。
將特定的狀態相關的行為都放入一個對象中,由於所有與狀態相關的代碼都存在於某個ConcreteState中,所以通過定義新的子類可以很容易地增加新的狀態和轉換
狀態模式通過把各種狀態轉換轉移邏輯分布到State的子類之間,來減少相互間的依賴
當一個對象的行為取決於它的狀態,並且它必須在運行時刻根據狀態改變它的行為時,就可以考慮使用狀態模式了。

適配器模式
定義:將一個類的接口轉換成客戶希望的另外一個接口。Adapter模式使得原本由於接口不兼容而不能一起工作的那些類可以一起工作。
系統的數據和行為都正確,但接口不符時,我們應該考慮用適配器,目的是使控制範圍之外的一個原有對象與某個對象接口匹配。適配器模式主要應用於希望復用一些現存的類,但是接口又與復用環境要求不一致的情況。
使用一個已經存在的類,但如果它的接口,也就是它的方法和你的要求不相同時,就應該考慮用適配器模式
兩個類所做的事情相同或相似,但是具有不同的接口時要使用它。
客戶端代碼可以統一調用同一接口就行了,這樣應該可以更簡單、更直接、更緊湊。
在雙方都不太容易修改的時候再使用適配器模式適配
DataAdapter用作DataSet和數據源之間的適配器以便檢索和保存數據。DataAdapter通過映射Fill(這更改了DataSet中的數據以便與數據源中的數據相匹配)和Update(這更改了數據源中的數據以便與DataSet中的數據相匹配)來提供這一適配器[MSDN]

備忘錄模式
定義:在不破壞封裝性的前提下,捕獲一個對象的內部狀態,並在該對象之外保存這個狀態。這樣以後就可將該對象恢復到原先保存的狀態。
如果在某個系統中使用命令模式時,需要實現命令的撤銷功能,那麽命令模式可以使用備忘錄模式來存儲可撤銷操作的狀態
當角色的狀態改變的時候,有可能這個狀態無效,這時候就可以使用暫時存儲起來的備忘錄將狀態復原

組合模式
定義:將對象組合成樹形結構以表示“部分-整體”的層次結構。組合模式使得用戶對單個對象和組合對象的使用具有一致性。
當你發現需求中是體現部分與整體層次的結構時,以及你希望用戶可以忽略組合對象與單個對象的不同,統一地使用組合結構中的所有對象時,就應該考慮組合模式了。
組合模式定義了包含基本對象、組合對象的類層次結構。基本對象可以被組合成更復雜的組合對象,而這個對象又可以被組合,這樣不斷地遞歸下去,客戶代碼中,任何用到基本對象的地方都可以使用組合對象了。
用戶是不用關心到底是處理一個葉節點還是處理一個組合組件,也就用不著為定義組合而寫一些選擇判斷語句了。
組合模式讓客戶可以一致地使用組合結構和單個對象。

叠代器模式
定義:提供一種方法順序訪問一個聚合對象中各個元素,而又不暴露該對象的內部表示
當你需要訪問一個聚集對象,而且不管這些對象是什麽都需要遍歷的時候,你就應該考慮用叠代器模式。
為遍歷不同的聚集結構提供如開始、下一個、是否結束、當前哪一項等統一的接口。
當你需要對聚集有多種方式遍歷時,可以考慮用叠代器模式。
叠代器(Iterator)模式就是分離了集合對象的遍歷行為,抽象出一個叠代器類來負責,這樣既可以做到不暴露集合內部結構,又可讓外部代碼透明地訪問集合內部的數據。

單例模式
定義:保證一個類僅有一個實例,並提供一個訪問它的全局訪問點。
所有類都有構造方法,不編碼則系統默認生成空的構造方法,若有顯示定義的構造方法,默認的構造方法就會失效。
通常我們可以讓一個全局變量使得一個對象被訪問,但它不能防止你實例化多個對象。一個最好的辦法就是,讓類自身負責保存它的唯一實例。這個類可以保證沒有其他實例可以被創建,並且它可以提供一個訪問該實例的方法。
單例模式因為Singleton類封裝它的唯一實例,這樣它可以嚴格地控制客戶怎樣訪問它以及何時訪問它。簡單地說就是對唯一實例的受控訪問。
lock是確保當一個線程位於代碼的臨界區時,另一個線程不進入臨界區。如果其他線程試圖進入鎖定的代碼,則它將一直等待(即被阻止),直到該對象被釋放[MSDN]。
class Singleton { //雙重鎖定
private static Singleton instance;
private static readonly object syncRoot = new object();
private Singleton(){}
public static Singleton GetInstance(){
if(instance == null){ //先判斷實例是否存在,不存在再加鎖處理
lock(syncRoot){
if(instance == null) instance = new Singleton();
}
}
return instance;
}
}
這樣的話,我們不用讓線程每次都加鎖,而只是在實例未被創建的時候再加鎖處理。同時也能保證多線程的安全。這種做法被稱為Double-Check Locking(雙重鎖定)。
C#與公共語言運行庫也提供了一種‘靜態初始化’方法,這種方法不需要開發人員顯式地編寫線程代碼,即可解決多線程環境下它是不安全的問題[MSDN]。
//下一行的sealed,阻止發生派生,而派生可能會增加實例
pubic sealed class Singleton{
//在第一次引用類的任何成員時創建實例。公共語言運行庫負責處理變量初始化
private static readonly Singleton instance = new Singleton();
private Singleton(){}
public static Singleton GetInstance(){
return instance;
}
}
由於這種靜態初始化方式是在自己被加載時就將自己實例化,所以被形象地稱之為餓漢式單例類。原先的單例模式處理方式是要在第一次被引用時,才會將自己實例化,所以就被稱為懶漢式單例類。

橋接模式
對象的繼承關系是在編譯時就定義好了,所以無法在運行時改變從父類繼承的實現。子類的實現與它的父類有非常緊密的依賴關系,以至於父類實現中的任何變化必然會導致子類發生變化。當你需要復用子類時,如果繼承下來的實現不適合解決新的問題,則父類必須重寫或被其他更適合的類替換。這種依賴關系限制了靈活性並最終限制了復用性。
合成/聚合復用原則:盡量使用合成/聚合,盡量不要使用類繼承。
聚合表示一種弱的‘擁有’關系,體現的是A對象可以包含B對象,但B對象不是A對象的一部分;合成則是一種強的‘擁有’關系,體現了嚴格的部分和整體的關系,部分和整體的生命周期一樣。
合成/聚合復用原則的好處是:優先使用對象的合成/聚合將有助於你保持每個類被封裝,並被集中在單個任務上。這樣類和類繼承層次會保持較小規模,並且不太可能增長為不可控制的龐然大物。
定義:將抽象部分與它的實現部分分離,使它們都可以獨立地變化。
什麽叫抽象與它的實現分離,這並不是說,讓抽象類與其派生類分離,因為這沒有任何意義。實現指的是抽象類和它的派生類用來實現自己的對象。
實現系統可能有多角度分類,每一種分類都有可能變化,那麽就把這種多角度分離出來讓它們獨立地變化,減少它們之間的耦合。
只要真正深入地理解了設計原則,很多設計模式其實就是原則的應用而已,或許在不知不覺中就在使用設計模式了。

命令模式
‘行為請求者’與‘行為實現者’的緊耦合。
對請求排隊或記錄請求日誌,以及支持可撤銷的操作等行為時,‘行為請求者’與‘行為實現者’的緊耦合是不太適合的。
定義:將一個請求封裝為一個對象,從而使你可用不同的請求對客戶進行參數化;對請求排隊或記錄請求日誌,以及支持可撤銷的操作。
命令模式的優點:
第一,它能較容易地設計一個命令隊列;
第二,在需要的情況下,可用較容易地將命令記入日誌;
第三,允許接收請求的一方決定是否要否決請求。
第四,可以容易地實現對請求的撤銷和重做;
第五,由於加進新的具體命令類不影響其他的類,因此增加新的具體命令類很容易。
命令模式把請求一個操作的對象與知道怎麽執行一個操作的對象分割開。

敏捷開發原則告訴我們,不要為代碼添加基於猜測的、實際不需要的功能。如果你不清楚一個系統是否需要命令模式,一般就不要著急去實現它,事實上,在需要的時候通過重構實現這個模式並不困難,只有在真正需要如撤銷/恢復操作等功能時,把原來的代碼重構為命令模式才有意義。

職責鏈模式
定義:使多個對象都有機會處理請求,從而避免請求的發送者和接受者之間的耦合關系。將這個對象練成一條鏈,並沿著這條鏈傳遞該請求,直到有一個對象處理它為止。
當客戶提交一個請求時,請求是沿鏈傳遞直至有一個ConcreteHandler對象負責處理它。
使得接收者和發送者都沒有對方的明確信息,且鏈中的對象自己也並不知道鏈的結構。結果是職責鏈可簡化對象的相互連接,它們僅需保持一個指向其後繼者的引用,而不需要保持它所有的候選者的引用。這也就大大降低耦合度了。
隨時地增加或修改處理一個請求的結構。增強了給對象指派職責的靈活性。
一個請求極有可能到了鏈的末端都得不到處理,或者因為沒有正確配置而得不到處理。

中介者模式
盡管將一個系統分割成許多對象通常可以增加其可復用性,但是對象相互連接的激增又會降低其可復用性了。
大量的連接使得一個對象不可能在沒有其他對象的支持下工作,系統表現為一個不可分割的整體,所以,對系統的行為進行任何較大的改動就十分困難了。
通過中介者對象,可以將系統的網狀結構變成以中介者為中心的星形結構,每個具體對象不再通過直接的聯系與另一個對象發生相互作用,而是通過‘中介者’對象與另一個對象發生相互作用。中介者對象的設計,使得系統的結構不會因為新對象的引入而造成大量的修改工作。
定義:用一個中介對象來封裝一系列的對象交互。中介者使各對象不需要顯式地相互引用,從而使其耦合松散,而且可以獨立地改變它們之間的交互。
中介者模式很容易在系統中應用,也很容易在系統中誤用。當系統出現了‘多對多’交互復雜的對象群時,不要急於使用中介者模式,而要先反思你的系統在設計上是不是合理。
中介者的出現減少了各個同事的耦合,使得可以獨立地改變和復用各個同事類和中介者
由於把對象如何協作進行了抽象,將中介作為一個獨立的概念並將其封裝在一個對象中,這樣關註的對象就從對象各自本身的行為轉移到它們之間的交互上來,也就是站在一個更宏觀的角度去看待系統。
由於具體中介者控制了集中化,於是就把交互復雜性變為了中介者的復雜性,這就使得中介者會變得比任何一個具體同事都復雜。
中介者模式的優點來自集中控制,其缺點也是它,使用時是要考慮清楚。

享元模式
定義:運用共享技術有效地支持大量細粒度的對象。
享元模式可以避免大量非常相似類的開銷。在程序設計中,有時需要生成大量細粒度的類實例來表示數據。如果能發現這些實例除了幾個參數外基本上都是相同的,有時就能夠大幅度地減少需要實例化的類的數量。如果能把那些參數移到類實例的外面,在方法調用時將它們傳遞進來,就可以通過共享大幅度地減少單個實例的數目。
如果一個應用程序使用了大量的對象,而大量的這些對象造成了很大的存儲開銷時就應該考慮使用;還有就是對象的大多數狀態可以是外部狀態,如果刪除對象的外部狀態,那麽可以用相對較少的共享對象取代很多組對象,此時可以考慮使用享元模式。

解釋器模式
定義:給定一個語言,定義它的文法的一種表示,並定義一個解釋器,這個解釋器使用該表示來解釋語言中的句子。
解釋器模式要解決的是:如果一種特定類型的問題發生的頻率足夠高,那麽可能就值得將該問題的各個實例表述為一個簡單語言中的句子。這樣就可以構建一個解釋器,該解釋器通過解釋這些句子來解決該問題。
當有一個語言需要解釋執行,並且你可將該語言中的句子表示為一個抽象語法樹時,可使用解釋器模式。
用了解釋器模式,就意味著可以很容易地改變和擴展文法,因為該模式使用類來表示文法規則,你可使用繼承來改變或擴展文法。也比較容易實現文法,因為定義抽象語法樹中各個節點的類的實現大體類似,這些類都易於直接編寫。
解釋器模式也有不足的,解釋器模式為文法中的每一條規則至少定義了一個類,因此包含許多規則的文法可能難以處理和維護。建議當文法非常復雜時,使用其他的技術如語法分析程序或編譯器生成器來處理。

訪問者模式
定義:表示一個作用於某對象結構中的各元素的操作。它使你可以在不改變各元素的類的前提下定義作用於這些元素的新操作。
訪問者模式適用於數據結構相對穩定的系統。它把數據結構和作用於結構上的操作之間的耦合解脫開,使得操作集合可以相對自由地演化。
訪問者模式的目的是要把處理從數據結構分離出來。很多系統可以按照算法和數據結構分開,如果這樣的系統有比較穩定的數據結構,又有易於變化的算法的話,使用訪問者模式就是比較合適的,因為訪問者模式使得算法操作的增加變得容易。反之,如果這樣的系統的數據結構易於變化,經常要有新的數據對象增加進來,就不適合使用訪問者模式。
訪問者模式的優點就是增加新的操作很容易,因為增加新的操作就意味著增加一個新的訪問者,訪問者模式將有關的行為集中到一個訪問者對象中。
訪問者模式的缺點其實就是使增加新的數據結構變得困難了。

模式總結

1.創建型模式
抽象工廠,提供一個創建一系列或相關依賴對象的接口,而無需指定它們具體的類。
建造者,將一個復雜對象的構建與它的表示分離,使得同樣的構建過程可以創建不同的表示。
工廠方法,定義一個用於創建對象的接口,讓子類決定實例化哪一個類,工廠模式使一個類的實例化延遲到其子類。
原型,用原型實例指定創建對象的種類,並且通過拷貝這些原型創建新的對象。
單例,保證一個類僅有一個實例,並提供一個訪問它的全局訪問點。

創建型模式隱藏了這些類的實例是如何被創建和放在一起,整個系統關於這些對象所知道的是由抽象類所定義的接口。這樣,創建型模式在創建了什麽、誰創建它、它是怎麽被創建的,以及何時創建這些方面提供了很大的靈活性。
建立相應數目的原型並克隆它們通常比每次用合適的狀態手工實例化該類更方便一些。
內聚性描述的是一個例程內部組成部分之間相互聯系的緊密程度。而耦合性描述的是一個例程與其他例程之間聯系的緊密程度。軟件開發的目標應該是創建這樣的例程:內部完整,也就是高內聚,而與其他例程之間的聯系則是小巧、直接、可見、靈活的,這就是松耦合。
建造者模式將一個復雜對象的構建與它的表示分離,這就可以很容易地改變一個產品的內部表示,並且使得構造代碼和表示代碼分開。這樣對於客戶來說,它無需關心產品的創建過程,而只要告訴我需要什麽,我就能用同樣的構建過程創建不同的產品給客戶。

單例模式:對一些類來說,一個實例是很重要的。讓類自身負責保存它的唯一實例。這個類可以保證沒有其他實例可以被創建,並且我還提供了一個訪問該實例的方法。對唯一的實例可以嚴格地控制客戶怎樣以及何時訪問它。
創建型模式抽象了實例化的過程。它們幫助一個系統獨立於如何創建、組合和表示它的那些對象。創建型模式都會將關於該系統使用哪些具體的類的信息封裝起來。允許客戶用結構和功能差別很大的‘產品’對象配置一個系統。配置可以是靜態的,即在編譯時指定,也可以是動態的,就是運行時再指定。

通常設計是從工廠方法開始,當設計者發現需要更大的靈活性時,設計便會向其他創建型模式演化。當設計者在設計標準之間進行權衡的時候,了解多個創建型模式可以給設計者更多的選擇余地。

2.結構型模式
適配器,將一個類的接口轉換成客戶希望的另外一個接口。適配器模式使得原本由於接口不兼容而不能一起工作的那些類可以一起工作。
使用一個已經存在的類,而它的接口不符合要求,或者希望創建一個可以復用的類,該類可以與其他不相關的類或不可預見的類協同工作。
主要是為了解決兩個已有接口之間不匹配的問題,適配器模式不需要考慮這些接口是怎樣實現的,也不考慮它們各自可能會如何演化。適配器模式的這種方式不需要對兩個獨立設計的類中任一個進行重新設計,就能夠使它們協同工作。

橋接,將抽象部分與它的實現部分分離,使它們可以獨立地變化。
解耦這些不同方向的變化,通過對象組合的方式,把兩個角色之間的繼承關系改為了組合的關系,從而使這兩者可以應對各自獨立地變化,即‘找出變化並封裝之’

組合,將對象組合成樹形結構以表示‘部分-整體’的層次結構,組合模式使得用戶對單個對象和組合對象的使用具有一致性。
客戶可以一致地使用組合結構和單個對象。任何用到基本對象的地方都可以使用組合對象。

裝飾,動態地給一個對象添加一些額外的職責。就增加功能來說,裝飾模式比生成子類更加靈活。
以動態、透明的方式給單個對象添加職責

外觀,為子系統中的一組接口提供一個一致的界面,外觀模式定義了一個高層接口,這個接口使得這一子系統更加容易使用。
如果兩個類不必彼此通信,那麽就不要讓這兩個類發生直接的相互作用。應該讓一個軟件中的子系統間的通信和相互依賴關系達到最小,而具體辦法就是引入一個外觀對象,它為子系統提供了一個單一而簡單的屏障。

享元,運用共享技術有效地支持大量細粒度的對象。
對象使得內存占用過多,而且如果都是大量重復的對象,那就是資源的極大浪費。

代理,為其他對象提供一種代理以控制對這個對象的訪問。
代理與外觀的主要區別在於,代理對象代表一個單一對象而外觀對象代表一個子系統;代理的客戶對象無法直接訪問目標對象,由代理提供對單獨的目標對象的訪問控制,而外觀的客戶對象可以直接訪問子系統中的各個對象,但通常由外觀對象提供對子系統各元件功能的簡化的共同層次的調用接口。
代理是一種原來對象的代表,其他需要與這個對象打交道的操作都是和這個代表交涉。而適配器則不需要虛構出一個代表者,只需要為應付特定使用目的,將原來的類進行一些組合。

3.行為型模式第一組
觀察者,定義對象間的一種一對多的依賴關系,當一個對象的狀態發生改變時,所有依賴他的對象都得到通知並被自動更新。
模板方法,定義一個操作的算法骨架,而將一些步驟延遲到其子類中,模板方法使得子類可以不改變一個算法的結構即可重定義該算法的某些特定步驟。
命令,將一個請求封裝為一個對象,從而使你可用不同的請求對客戶進行參數化;可以對請求排隊或記錄請求日誌,以及支持可撤銷的操作。
狀態,允許一個對象在其內部狀態改變時改變它的行為,讓對象看起來似乎修改了它的類。
職責鏈,使多個對象都有機會處理請求,從而避免請求的發送者和接收者之間的耦合關系。將這些對象連成一條鏈,並沿著這條鏈傳遞該請求,直到有一個對象處理它為止。

代碼重復是編程中最常見、最糟糕的‘壞味道’,如果我們在一個以上的地方看到相同的程序結構,那麽可以肯定,設法將它們合而為一,程序會變得更好。完全相同的代碼當然存在明顯的重復,而微妙的重復會出現在表面不同但本質相同的結構或處理步驟中。

模板方法模式由一個抽象類組成,這個抽象類定義了需要覆蓋的可能有不同實現的模板方法,每個從這個抽象類派生的具體類將為此模板實現新方法。

命令模式,將調用操作的對象與知道如何實現該操作的對象解耦,支持在不同的時刻指定、排列和執行請求,支持取消/重做的操作。還可以記錄整個操作的日誌,支持事務。

職責鏈模式,有多個對象可以處理一個請求,哪個對象處理該請求事先並不知道,要在運行時刻自動確定,讓客戶在不明確指定接收者的情況下,提交一個請求,然後由所有能處理這個請求的對象連成一條鏈,並沿著這條鏈傳遞該請求,直到有一個對象處理它為止。

狀態模式提供了一個更好的辦法來組織與特定狀態相關的代碼,決定狀態轉移的邏輯不在單塊的if或switch中,而是分布在各個狀態子類之間,由於所有與狀態相關的代碼都存在於某個狀態子類中,所以通過定義新的子類可以很容易地增加新的狀態和轉換。

MVC是包括三類對象,Model是應用對象,View是它在屏幕是上的表示,Controller定義用戶界面對用戶輸入的響應方式。如果不使用MVC,則用戶界面設計往往將這些對象混在一起,而MVC則將它們分離以提高靈活性和復用性。
MVC是多種模式的綜合應用,應該算是一種架構模式。

4.行為型模式第二組
解釋器模式,給定一個語言,定義它的文法的一種表示,並定義一個解釋器,這個解釋器使用該表示來解釋語言中的句子。
如果一種特定類型的問題發生的頻率足夠高,那麽就可以考慮將該問題的各個實例表述為一個簡單語言中的句子。也就是說,通過構建一個解釋器,該解釋器解釋這些句子來解決該問題。

中介者模式,用一個中介對象來封裝一系列的對象交互。中介者使各對象不需要顯式地相互引用,從而使其耦合松散,而且可以獨立地改變它們之間的交互。
面向對象設計鼓勵將行為分布到各個對象中,這種分布可能會導致對象間有許多連接。也就是說,有可能每一個對象都需要知道其他許多對象。對象間的大量相互連接使得一個對象似乎不太可能在沒有其他對象的支持下工作,這對於應對變化是不利的,任何較大的改動都很困難。
將集體行為封裝在一個單獨的中介者對象來避免這個問題,中介者負責控制和協調一組對象間的交互。中介者充當一個中介以使組中的對象不再相互顯式引用。這些對象僅知道中介者,從而減少了相互連接的數目。
最少知識原則,也就是如何減少耦合的問題,類之間的耦合越弱,越有利於復用。

訪問者模式,表示一個作用於某對象結構中的各元素的操作。它使你可以在不改變各元素的類的前提下定義作用於這些元素的新操作。
訪問者增加具體Element是困難的,但增加依賴於復雜對象結構的構件的操作就變得容易。僅需增加一個新的訪問者即可在一個對象結構上定義一個新的操作。

策略模式,定義一系列的算法,把它們一個個封裝起來,並且使它們可相互替換。本模式使得算法可獨立於使用它的客戶而變化。
繼承提供了一種支持多種算法或行為的方法,我們可以直接生成一個類A的子類B、C、D,從而給它以不同的行為。但這樣會將行為硬性編制到父類A當中,而將算法的實現與類A的實現混合起來,從而使得類A難以理解、難以維護和難以擴展,而且還不能動態地改變算法。仔細分析會發現,它們之間的唯一差別是它們所使用的算法或行為,將算法封裝在獨立的策略Strategy類中使得你可以獨立於其類A改變它,使它易於切換、易於理解、易於擴展。

備忘錄模式,在不破壞封裝性的前提下,捕獲一個對象的內部狀態,並在該對象之外保存這個狀態。這樣以後就可將該對象恢復到原先保存的狀態。
使用備忘錄模式,可以避免暴露一些只應當由對象A管理卻又必須存儲在對象A之外的信息。備忘錄模式把可能很復雜的對象A的內部信息對其他對象屏蔽起來,從而保持了封裝邊界。

叠代器模式,提供一種方法順序訪問一個聚合對象的中的各個元素,而又不暴露該對象的內部表示。
叠代器模式的關鍵思想是將對列表的訪問和遍歷從列表對象中分離出來並放入一個叠代器對象中,叠代器類定義了一個訪問該列表元素的接口。叠代器對象負責跟蹤當前的元素,並且知道哪些元素已經遍歷過了。

5.其他
只要是在做面向對象的開發,創建對象的工作不可避免。創建對象時,負責創建的實體通常需要了解要創建的是哪個具體的對象,以及何時創建這個而非那個對象的規則。而我們如果希望遵循開放-封閉原則、依賴倒轉原則和裏氏替換原則,那使用對象時,就不應該知道所用的是哪一個特選的對象。此時就需要‘對象管理者’工廠來負責此事。
在創建對象時,使用抽象工廠、原型、建造者的設計比使用工廠方法要更靈活,但它們也更加復雜。通常,設計是以使用工廠方法開始,當設計者發現需要更大的靈活性時,設計便會向其他創建型模式演化。
工廠方法的實現並不能減少工作量,但是它能夠在必須處理新情況時,避免使已經很復雜的代碼更加復雜。
面向對象的設計模式體現的就是抽象的思想,類是對對象的抽象,抽象類是對類的抽象,接口是對行為的抽象。
泛型是具有占位符(類型參數)的類、結構、接口和方法,這些占位符是類、結構、接口和方法所存儲或使用的一個或多個類型的占位符。泛型集合類可以將類型參數用作它所存儲的對象的類型的占位符;類型參數作為其字段的類型和其方法的參數類型出現[MSDN]。

UML類圖概述、設計模式