讀完了這本書,收穫很多,對程式設計有了很多新的理解。將每章模式的大概要點做了些筆記以備查閱,一些設計模式書讀完也對其瞭解得不是很透徹,需要以後在實踐中來不斷地加深理解吧。讀書過程中用Java跟著實踐了些部分模式的代碼上傳到了 "https://github.com/wanghaoxi3000/des ...
讀完了<大話設計模式>這本書,收穫很多,對程式設計有了很多新的理解。將每章模式的大概要點做了些筆記以備查閱,一些設計模式書讀完也對其瞭解得不是很透徹,需要以後在實踐中來不斷地加深理解吧。讀書過程中用Java跟著實踐了些部分模式的代碼上傳到了https://github.com/wanghaoxi3000/design_mode。
設計模式
簡單工廠模式
簡單工廠模式是屬於創建型模式,又叫做靜態工廠方法(Static Factory Method)模式,但不屬於23種GOF設計模式之一。簡單工廠模式是由一個工廠對象決定創建出哪一種產品類的實例。簡單工廠模式是工廠模式家族中最簡單實用的模式,可以理解為是不同工廠模式的一個特殊實現。
UML圖
策略模式
策略模式定義了演算法家族,分別封裝起來,讓他們之間可以相互替換,此模式讓演算法的變化,不會影響到使用演算法的客戶。
UML圖
裝飾模式
動態地給一個對象添加一些額外的職責,就增加功能來說,裝飾模式比生成子類更為靈活。
裝飾模式是為已有功能動態地添加更多功能的一種方式。當系統需要新功能的時候,是向舊的類添加新的代碼。這些新的代碼通常創世了原有類的核心職責或主要行為。
UML圖
代理模式
為其他對象提供一種代理以控制這個對象的訪問。
UML圖
代理模式的應用
- 遠程代理:為一個對象在不同的地址空間提供局部代表。這樣可以隱藏一個對象存在於不同地址空間的事實。
- 虛擬代理:根據需要創建開銷很大的對象。通過它來實例化需要很長時間的真實對象。
- 安全代理:用來控制真實對象訪問時的許可權。
- 智能指引:當調用真實對象時,代理處理另外一些事。如在訪問一個實際對象前,檢查是否已經鎖定它,以確保其他對象不能改變它。
工廠方法模式
定義一個用於創建對象的介面,讓子類決定實例化哪一個類。工廠方法使一個類的實例化延遲到子類。
結構圖
UML圖
簡單工廠與工廠方法的對比
簡單工廠模式的最大優點在於工廠類中包含了必要的邏輯判斷,根據客戶端的選擇條件動態實例化相關的類,對客戶端來說,去除了與具體產品的依賴。但新增功能時,需要修改原有的類。不但對擴展開放了,對修改也開放了,違背了開放-封閉原則。
工廠方法模式實現時,客戶端需要決定實例化哪一個工廠來實現運算類,選擇判斷的問題還是存在的,也就是說,工廠方法把簡單工廠內部的判斷邏輯移到了客戶端代碼來進行。
原型模式
用原型實例指定創建對象的種類,並且通過拷貝這些原型創建新的對象
UML圖
原型模式其實就是從一個對象創建另外一個可定製的對象,而且不需要知道任何創建的細節。
Java提供的克隆介面
Java的所有類都是從java.lang.Object類繼承而來的,而Object類提供protected Object clone()方法對對象進行複製,子類當然也可以把這個方法置換掉,提供滿足自己需要的複製方法。對象的複製有一個基本問題,就是對象通常都有對其他的對象的引用。當使用Object類的clone()方法來複制一個對象時,此對象對其他對象的引用也同時會被覆制一份
Java語言提供的Cloneable介面只起一個作用,就是在運行時期通知Java虛擬機可以安全地在這個類上使用clone()方法。通過調用這個clone()方法可以得到一個對象的複製。由於Object類本身並不實現Cloneable介面,因此如果所考慮的類沒有實現Cloneable介面時,調用clone()方法會拋出CloneNotSupportedException異常。
淺克隆和深克隆
無論你是自己實現克隆方法,還是採用Java提供的克隆方法,都存在一個淺度克隆和深度克隆的問題。
- 淺度克隆:只負責克隆按值傳遞的數據(比如基本數據類型、String類型),而不複製它所引用的對象,換言之,所有的對其他對象的引用都仍然指向原來的對象。
- 深度克隆:除了淺度克隆要克隆的值外,還負責克隆引用類型的數據。那些引用其他對象的變數將指向被覆制過的新對象,而不再是原有的那些被引用的對象。換言之,深度克隆把要複製的對象所引用的對象都複製了一遍,而這種對被引用到的對象的複製叫做間接複製。
深度克隆要深入到多少層,是一個不易確定的問題。在決定以深度克隆的方式複製一個對象的時候,必須決定對間接複製的對象時採取淺度克隆還是繼續採用深度克隆。因此,在採取深度克隆時,需要決定多深才算深。此外,在深度克隆的過程中,很可能會出現迴圈引用的問題,必須小心處理。
模板方法模式
定義一個操作中的演算法的骨架,而將一些步驟延遲到子類中。模板方法使得子類可以不改變一個演算法結構即可重定義該演算法的的某些特定步驟
UML圖
特點
模板方法模式是通過把不變的行為搬移到超類,去除子類中的重覆代碼來體現它的優勢。當不變的和可變的行為在方法的子類實現中混合在一起的時候,不變的行為就會在子類中重覆出現,我們通過模板模式把這些行為搬到單一的地方,這樣就幫助子類擺脫重覆的不變行為的糾纏。
外觀模式
為子系統的一組介面提供一個一致的界面,此模式定義了一個高層介面,這個介面使這一子系統更加容易使用。
UML圖
何時使用外觀模式
- 在設計初期階段,應該要有意識的將不同的兩個層分離,層與層之間建立Facade
- 在開發階段,子系統往往因為不斷重構演化而變得越來越複雜,增加外觀Facade可以提供一個簡單的介面,減少他們之間的依賴
- 維護一個遺留的大型系統時,可能這個系統已經非常難以維護和擴展了,可以為新系統開發一個外觀Facade類,讓新系統與Facade對象交互,Facade與遺留代碼交互完成所有複雜的工作
建造者模式
建造者模式是在當創建複雜對象的演算法應該獨立於該對象的組成部分以及它們的裝配方式時適用的模式。
UML圖
何時使用建造者模式
建造者模式主要用於創建一些複雜的對象,這些對象內部構建間的構造順序通常是穩定的,但對象內部的構建通常面臨著複雜的變化。
建造者模式使得建造代碼與表示代碼分離,由於建造者隱藏了該產品是如何組裝的,所以需要改變一個產品的內部表示,只需要再定義一個具體的建造者就可以了。
觀察者模式
觀察者模式又稱之為發佈-訂閱模式,定義了一種一對多的依賴關係,讓多個觀察者對象同時監聽一個主題對象。這個主題對象在狀態發生變化時,會通知所有觀察者對象,使它們能夠自動更新自己。
UML圖
觀察者模式的特點
將一個系統分割成一系列相互協作的類有一個很不好的副作用,需要維護相關對象間的一致性。我們不希望為了維持一致性而使各類緊密耦合,這樣會給維護,擴展和重用都帶來不便。當一個對象的改變需要同時改變其他對象,而且它不知道具體有多少對象待改變時,應該考慮使用觀察者模式。
一個抽象模型有兩個方面,其中一方面依賴於另一方面,這時使用觀察者模式可以將這兩者封裝在獨立的對象中使它們各自獨立的改變和復用。
觀察者模式所做的工作其實就是在解除耦合。讓耦合的雙方都依賴於抽象,而不是依賴於具體。從而使各自的變化都不會影響另一邊的變化。
觀察者模式的不足與事件委托
觀察者模式需要觀察者具體類們實現一個通用的通知方法時,當各個觀察者無法實現這個通用的通知方法或通知方法不同名時,可使用事件委托,讓客戶端來指定具體的通知方法。
抽象工廠模式
提供一個創建一系列相關或相互依賴對象的介面,而無需指定它們具體的類。
UML圖
AbstractProductA和AbstractProductB是兩個抽象產品,ProductA1、ProductA2和ProductB1、ProductB2就是對兩個抽象產品的具體分類實現。
IFactory是一個抽象工廠介面,它裡面應該包含所有產品創建的抽象方法。ConcreteFactory1和ConcreteFactory2就是具體的工廠了。
通常在運行時再創建一個ConcreteFactory類的實例,這個具體的工廠再創建具有特定實現的產品對象,也就是說,為創建不同的產品對象,客戶端應使用不同的具體工廠。
抽象工廠的優缺點
優點:
- 具體工廠類在一個應用中只需要初始化時出現一次這就使得改變一個應用的具體工廠變得非常容易
- 它讓具體的創建實例過程與客戶端分離,客戶端是通過它們的抽象介面操作實例,產品的具體類名也被具體工廠實現分離,不會出現在客戶代碼中
缺點:
- 增加功能時需要改動和增加的類較多
- 客戶端程式切換不同工廠時改動較多
狀態模式
當一個對象的內在狀態改變時允許改變其行為,這個對象看起來像是改變了其類。
狀態模式主要解決的是控制一個對象狀態轉換的條件表達式過於複雜時的情況。把狀態的判斷邏輯轉移到表示不同狀態的一系列類當中,可以把複雜的判斷邏輯簡化。當然,如果這個判斷邏輯很簡單,那就沒必要用‘狀態模式’了。
UML圖
狀態模式的好處與用處
狀態模式的好處是將與特定狀態的相關行為局部化,並且將不同的行為分割開來。
通過將特定的狀態相關行為都放入一個對象中,由於所有與狀態相關的代碼都存於某個ConcreteState中,所以通過定義新的子類可以很容易的增加新的狀態和轉換。從而消除龐大的條件分支語句。
狀態模式同個把各種狀態轉移邏輯分佈到State子類之間,來減少相互之間的依賴,當一個對象的行為取決它的狀態,並且它必須在運行時刻根據狀態改變它的行為時,就可以考慮使用狀態模式了。
適配器模式
將一個類的介面轉換成客戶希望的另外一個介面。Adapter模式使得由於介面不相容而不能一起工作的那些類可以一起工作。
在軟體開發中,系統的數據和行為都正確,但介面不符時,應該考慮用適配器。使控制範圍之外的一個原有對象與某個介面匹配。適配器主要應用於希望復用一些現存的類,但是介面又與復用環境要求不一致的情況。
UML圖
慎用適配器模式
當兩個類所做的事情相同或相似,但是具有不同的介面時要使用它。但應在雙方都不太容易修改的時候再使用適配器模式。儘量使用統一調用同一介面使得更為簡單緊湊。
備忘錄模式
在不破壞封裝性的前提下,捕獲一個對象的內部狀態,併在該對象之外保存這個狀態。這樣以後就可將該對象恢復到原先保存的狀態。
UML圖
適用場合
備忘錄模式比較適用於功能比較複雜的,但需要維護或記錄屬性歷史的類,或者需要保存的屬性只是眾多屬性中的一小部分時。使用備忘錄可以把複雜的對象內部信息屏蔽起來。
組合模式
將對象組合成樹形結構以表示‘部分-整體’的層次結構。組合模式將使得對象對單個用戶對單個對象和組合對象的使用具有一致性。
UML圖
適用場合
當需求中是體現部分與整體的層次結構時,以及希望用戶可以忽略組合對象與單個對象的不同,統一地使用組合結構中的所有對象時,就應該考慮組合模式了。通過組合模式可以一致地使用組合結構和單個對象。
迭代器模式
提供一種方法順序訪問一個聚合對象中的各個元素,而又不暴露該對象的內部表示。
當需要訪問一個聚集對象,而且不管這些對象是什麼都需要遍歷的時候,此時就應該考慮迭代器模式。
當需要對聚集對象進行多種方式遍歷時,也可以考慮迭代器模式。
即為遍歷不同的聚集結構提供如開始、下一個、是否結束、當前哪一項等統一介面。
迭代器模式分離了集合對象的遍歷行為,抽象出一個迭代器類來負責,這樣既可以做到不暴露集合內部結構,又可以讓外部代碼透明地訪問集合內部數據。
UML圖
單例模式
保證一個類僅有一個實例,並提供一個訪問它的全局訪問點。
通常我們可以讓一個全局變數通過一個對象被訪問,但它不能防止你實例化多個對象。一個最好的辦法就是,讓類的自身負責保存它唯一的實例。這個類可以保證沒有其他實例可以被創建,並且提供一個訪問該實例的方法。
UML圖
橋接模式
將抽象部分與它的實現部分分離,使它們都可以獨立地變化。
抽象與它的實現分離,這並不是說,讓抽象類與其派生類分離,因為這沒有任何意義。實現指的是抽象類和它的派生類用來實現自己的對象。
UML圖
命令模式
將一個請求封裝為一個對象,從而使你可以用不同的請求對客戶進行參數化,對請求派對或記錄請求日誌,以及支持可撤銷的操作。
UML圖
命令模式作用
- 可以比較容易設計一個命令隊列
- 在需要的情況下,可以較容易地將命令記入日誌
- 允許接收請求的一方決定是否要否決請求
- 可以容易的實現對請求的撤銷和重做
- 由於加進新的具體命令類不影響其他的類,因此增加新的具體命令類就很容易
職責鏈模式
使多個對象都有機會處理請求,從而避免請求的發送者和接受者之間的耦合關係。將這個對象連成一條鏈,並沿著這條鏈傳遞該請求,直到一個對象處理它為止。
UML圖
職責鏈的優點
職責鏈模式使得接收者和發送者都沒有對方的明確信息,而且鏈中的對象自己也並不知道鏈的結構。結構是職責鏈可簡化對象的相互連接,它們僅需保持一個指向後繼者的引用,而不保持它所有的候選接受者的相互連接,它們僅需保持一個指向其後繼者的引用,而不需保持它所有的候選者的引用。
中介者模式
用一個中介對象來封裝一系列的對象交互。中介者使各對象不需要顯式地相互引用,從而使其耦合鬆散,而且可以獨立的改變它們之間的交互。
享元模式
運用共用技術有效的支持大量細粒度的對象。享元模式可以避免大量非常相似的開銷。在程式設計中,有時需要生成大量細粒度的類實例來顯示數據。如果發現這些實例除了幾個參數外基本都是相同的,有時就能夠大幅度地減少需要實例化的類數量。如果把那些參數移到類實例的外面,在方法調用時將它們傳遞進來,就可以通過共用大幅度的減少單個實例的數目。
UML圖
享元模式的應用
如果一個應用程式使用了大量的對象,而大量的這些對象造成了很大的存儲開銷時就應該考慮使用,還有就是對象的大多數狀態可以是外部狀態,如果刪除對象的外部狀態,那麼就可以用相對較少的共用對象取代很多組對象,此時就可以考慮使用享元模式。
解釋器模式
給定一個語言,定義它的文法的一種表示,並定義一個解釋器,這個解釋器使用該表示來解釋語言中的句子。
解釋器所要解決的是一種特定類型的問題發生的頻率足夠高,那麼可能值得將該問題的各個實例表述一個簡單語言中的句子。這樣就可以構建一個解釋器,該解釋器通過解釋這些句子來解決該問題。
UML圖
訪問者模式
表示一個作用於對象結構中的各元素的操作。它使你可以在不改變各元素的類的前提下定義作用於這些元素的新操作。
UML圖
訪問者模式優缺點
訪問者模式的目的是要把數據從數據結構中分離出來。一個系統有比較穩定的數據結構,又有易於變化的演算法的話,使用訪問者模式就是比較合適的,因為訪問者模式使演算法操作增加變得容易,將有關的行為集中到一個訪問者對象中。但缺點也是使增加新的數據結構變得困難了。
設計原則
- 單一職責原則(SRP):就一個類而言,應該僅有一個引起它變化的原因
- 開放-封閉原則:軟體實體(類、模塊、函數等等)應該可以擴展,但是不可修改
- 依賴倒轉原則:高層模塊不應該依賴低層模塊。兩個都應該依賴抽象;抽象不應該依賴細節。細節應該依賴抽象
- 里氏代換原則:子類型必須能夠替換掉他們的父類型
- 迪米特法則:如果兩個類不必彼此直接通信,那麼這兩個類就不應當發生直接的相互作用,可以通過第三者轉發這個應用
- 合成/聚合復用原則:儘量使用合成、聚合,儘量不要使用類繼承