深刻淺出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]。算法