由 楊柳依 創建於2019年11月3日,最近更新於2019年11月8日 參考資料: "大話設計模式" | "圖解設計模式" | "菜鳥教程—設計模式" UML類圖 【矩形框】代表一個類(Class)。類圖分三層: 第一層顯示類的名稱,如果是抽象類,則就用斜體顯示; 第二層是類的特性,通常就是欄位和屬 ...
由 楊柳依 創建於2019年11月3日,最近更新於2019年11月8日
UML類圖
【矩形框】代表一個類(Class)。類圖分三層:
第一層顯示類的名稱,如果是抽象類,則就用斜體顯示;
- 第二層是類的特性,通常就是欄位和屬性;
第三層是類的操作,通常是方法或行為。註意前面的符號,
+
表示public,-
表示private,#
表示 protected。
【線條】代表類之間的關係。
繼承:空心三角形+實線,鳥類繼承動物類
實現:空心三角形+虛線,鳥類實現飛翔介面
關聯:實線箭頭,企鵝類知道氣候類(企鵝類中引用了氣候對象)
聚合:空心菱形+實線箭頭,每隻大雁都是屬於一個雁群,一個雁群可以有多只大雁(雁群中引用了大雁數組對象)
組合:實心菱形+實線箭頭,翅膀是鳥的一部分,擁有相同的生命周期(初始化鳥對象時,同時實例化翅膀對象)
依賴:虛線箭頭,動物需要氧氣、水(氧氣和水是動物類某個方法的參數)
六大原則 + 一個法則
開放封閉原則:實現熱插拔,提高擴展性。
單一職責原則:一個類只負責一個職責。
里氏代換原則:實現抽象的規範,實現子父類互相替換。
依賴倒轉原則:針對介面編程,實現開閉原則的基礎。
介面隔離原則:降低耦合度,介面單獨設計,互相隔離。
合成復用原則:儘量使用聚合,組合,而不是繼承。
迪米特法則:功能模塊儘量獨立。
1. 開放封閉原則
開閉原則的意思是:對擴展開放,對修改關閉。軟體實體(類、模塊、函數等)應該可以擴展,但不可修改。即面對需求,對程式的改動是通過增加新代碼進行的,而不是更改現有的代碼。
以計算器程式為例,抽象出一個運算類,嗎,每當要增加新的運算方式時,只要修改這個抽象類和增加新的運算類,不會修改已有的運算類。
2. 單一職責原則
單一職責原則,就一個類而言,應該僅有一個引起它變化的原因。
以俄羅斯方塊為例,窗體顯示(界面)是一個類,方塊的移動控制(游戲邏輯)是另一個類,將不同的職責分離到不同的類上。
3. 里氏代換原則
里氏替換原則:子類型必須能夠替換掉它們的父類型。(多態)
里氏代換原則是面向對象設計的基本原則之一。 里氏代換原則中說,任何基類可以出現的地方,子類一定可以出現。LSP 是繼承復用的基石,只有當派生類可以替換掉基類,且軟體單位的功能不受到影響時,基類才能真正被覆用,而派生類也能夠在基類的基礎上增加新的行為。里氏代換原則是對開閉原則的補充。實現開閉原則的關鍵步驟就是抽象化,而基類與子類的繼承關係就是抽象化的具體實現,所以里氏代換原則是對實現抽象化的具體步驟的規範。
4. 依賴倒轉原則
也叫依賴倒置、依賴反轉。這個原則是開閉原則的基礎。
依賴倒轉原則,A)高層模塊不應該依賴低層模塊,兩個都應該依賴抽象;b)抽象不應該依賴細節,細節應該依賴抽象。即針對介面名稱,不要對實現編程。
例如,電腦可以很容易修理,因為電腦內部都是由一些(高內聚低耦合的)配件組成,壞了換一個新的就可;但是收音機很難修理,因為收音機裡面都是一些元器件耦合在一起。
5. 介面隔離原則
這個原則的意思是:使用多個隔離的介面,比使用單個介面要好。它還有另外一個意思是:降低類之間的耦合度。由此可見,其實設計模式就是從大型軟體架構出發、便於升級和維護的軟體設計思想,它強調降低依賴,降低耦合。
6. 合成復用原則
合成復用原則是指:儘量使用合成/聚合的方式,而不是使用繼承。
7. 迪米特法則
又稱最少知道原則,是指:一個實體應當儘量少地與其他實體之間發生相互作用,使得系統功能模塊相對獨立。
如果兩個類不必彼此直接通信,那麼這兩個類就不應當發生直接的相互作用。如果其中一個類需要調用另一個類的某一個方法,可以通過第三者轉發這個調用。
例如,公司小李去維修電腦,只需要去找IT部,讓IT部去找具體的人來維修電腦。
設計模式
1. 簡單工廠模式
不在 GoF23種設計模式中
傳入參數,由工廠對象去實例化實際的操作對象。
以計算器為例,傳入操作運算符(+
、-
、*
、/
)給工廠對象,由工廠對象去創建實際的運算對象(為了便於擴展,這裡不同的運算對應著不同的類,並且繼承著運算類),最後得到結果。
2. 策略模式
定義了演算法家族,分別封裝起來,讓它們之間可以互相替換,此模式讓演算法的變化,不會影響到使用演算法的客戶(多態)。
以超市打折為例,正常收費,打折收費和返利收費是具體策略,用一個類去管理具體策略;同時還可以與簡單工廠模式結合,傳入演算法名稱,由工廠去創建策略管理類。
【優點】
- 策略模式是一種定義一系列演算法的方法,從概念上來看,所有這些演算法完成的都是相同的工作,只是實現不同,它可以以相同的方式調用所有的演算法,減少了各種演算法類與使用演算法類之間的耦合。
- 策略模式的Strategy類層次為Context定義了一系列的可供重用的演算法或行為。繼承有助於析取出這些演算法中的公共功能。
- 公共的功能就是獲得計算費用的結果GetResult,這使得演算法間有了抽象的父類CashSuper。
- 簡化了單元測試,因為每個演算法都有自己的類,可以通過自己的介面單獨測試
3. 裝飾器模式
裝飾器模式,動態地給一個對象添加一些額外的職責,就增加功能來說,裝飾器模式比生成子類更加靈活。它把每個要裝飾的功能放在單獨的類中,並讓這個類包裝它所要裝飾的對象,在使用時要註意裝飾的順序。
以穿衣服為例,人為一個抽象類,有穿衣這個抽象方法,具體的男人類繼承人抽象類,有一個抽象穿衣類(裝飾器類)繼承人抽象類,有具體的穿上衣類、穿外套類、穿內褲類、穿外褲類繼承抽象穿衣類。先創建一個男人對象X、穿上衣對象A、穿內褲對象B等,將X設置到A中,再將A設置到B中,鏈式調用(註意是一層套一層的)。
4. 代理模式
代理模式,為其它對象提供一種代理以控制對這個對象的訪問。
例如,王小明通過戴勵給劉小紅送禮物,有
Subjec類:定義了RealSubject和Proxy的公用介面,這樣在任何使用RealSubject的地方都可以使用Proxy。【送禮物這一行為】
RealSubject類:定義Proxy所代表的真實實體。【王小明】
Proxy類:保存一個引用使得代理可以訪問真實實體,並提供一個與RealSubject內相同的訪問方法,這樣代理就可以用來替代真實實體。【戴勵】
5. 工廠方法模式
工廠模式方法,定義一個用於創建對象對象的介面,讓子類決定實例化哪一個類,工廠方法使一個類的實例化延遲到其子類。
以計算器程式為例,創建一個工廠介面,然後加減乘除各建一個具體的工廠去實現這個介面,每個具體工廠負責創建具體的運算對象。
6. 原型模式
原型模式,用原型實例指定創建對象的種類,並通過拷貝這些原型對象創建新的對象。
例如,有一個抽象原型類有抽象clone方法,然後有多個具體原型類去實現該clone方法,註意【淺拷貝與深拷貝】,原型中引用的對象,拷貝的時候只會拷貝引用,需要在引用的對象中也實現拷貝方法,然後在給原型賦值的是對象的拷貝。
7. 模板方法模式
定義一個操作中的演算法的骨架,將一些步驟延遲到子類中。模板方法使得子類可以不改變一個演算法的結構即可重定義該演算法的某些特定步驟。
比如,一個抽象動物類有抽象吃食物的方法,在一些模板方法可以調用這個抽象方法(例如吃完東西才會睡覺),具體的狗類、貓類、鳥類實現具體的吃食物的方法。
8. 外觀模式
外觀模式,為子系統中的一組介面提供一個一致的界面,此模式定義了一個高層介面,這個介面使得這一子系統更加容易使用。
外觀模式完美的體現了依賴倒轉原則和迪米特法則的思想。
例如股民炒股,股民需要知道多個股票的情況,耦合性過高,而把錢投在基金上,由基金來管理多支股票,用戶只用與基金打交道。
【何時使用】
- 階段1:在設計初期,有意識的將不同層的業務分離,比如經典的MVC架構;
- 階段2:在開發階段,子系統會因為不斷的重構演化而變得越來越複雜,增加外觀Facade可以提供一個簡單的介面,減少它們之間的依賴;
- 階段3:對於維護遺留系統而言,可以為新系統開發一個外觀Facade類,讓新系統與Facade對象交互,Facade與遺留系統交互完成所有複雜的工作。
9. 建造者模式
建造者模式(Builder),又叫生成器模式,將一個複雜對象的構建與它的表示分離,使得同樣的構建過程可以創建不同的表示。
例如,一個畫小人程式,可以畫出瘦人、胖人等,有
Product類:小人類,由多個部分組成。
Builder介面:建造小人各個部分的抽象類。
ConcreteBuilder類:具體建造者,實現Builder介面。具體實現如何畫出小人的頭身手腳各個部分。
Director類:指揮者,構建一個使用Builder介面的對象。用來根據用戶的需求構建小人對象。
10. 觀察者模式
觀察者模式,又叫發佈-訂閱(Publish/Subscribe)模式,定義了一種一對多的依賴關係,讓多個觀察者對象同時監聽某一個主題對象。這個主題對象在狀態發生變化時,會通知所有觀察者對象,使它們能夠自動更新自己。
例如,員工上班偷懶,需要前臺A或者前臺B望風,有
Subject類:抽象通知者類(主題)。一般用一個抽象類或者一個介面實現。它把所有對觀察者對象的引用保存在一個聚集里,每個主題都可以有任何數量的觀察者。抽象主題提供一個介面,可以增加和刪除觀察者對象。
Observer類:抽象觀察者,為所有的具體觀察者定義一個介面,在得到主題的通知時更新自己。這個介面叫做更新介面。抽象觀察者一般用一個抽象類或者一個介面實現。更新介面通常包含一個Update() 方法,這個方法叫做更新方法。
ConcreteSubject類:叫做具體主題或具體通知者,將有關狀態存入具體現察者對象;在具體主題的內部狀態改變時,給所有登記過的觀察者發出通知。具體主題角色通常用一個具體子類實現。
ConcreteObserver類:具體觀察者,實現抽象觀察者角色所要求的更新介面,以便使本身的狀態與主題的狀態相協調。具體觀察者角色可以保存一個指向具體主題對象的引用。具體觀察者角色通常用一個具體子類實現。
觀察者模式的關鍵對象是主題Subject和觀察者Observer,一個Subject可以有任意數目的依賴它的Observer,一旦Subject的狀態發生了改變,所有的Observer都可以得到通知。
觀察者模式所做的工作其實就是在解除耦合。讓耦合的雙方都依賴於抽象,而不是依賴於具體。從而使得各自的變化都不會影響另一邊的變化。
11. 抽象工廠模式
抽象工廠模式(Abstract Factory),提供一個創建一系列相關或相互依賴對象的介面,而無需指定它們具體的類。解決涉及到多個產品系列的問題,有一個專門的工廠模式叫抽象工廠模式。
例如,有一個業務需要訪問指定的資料庫(可能會變動),資料庫中又有幾張表,為了便於修改資料庫訪問方式,這時就需要使用抽象工廠模式。
抽象工廠介面:裡面應該包含所有的產品創建的抽象方法【創建資料庫的抽象方法】;
具體的工廠:實現抽象工廠介面,創建具有特定實現的產品對象【實例化對應的具體資料庫對象】;
抽象產品:它們都有可能有多種不同的實現【資料庫的訪問資源方式的抽象方法】;
具體產品:繼承抽象產品,對抽象產品的具體分類的實現【具體資料庫的訪問資源方法】。
【改進1】反射
Java反射:通過全限定類名去載入對應類的位元組碼(Class)文件
Class c = Class.forName("com.loveshes.designpattern.reflect.User");
Constructor con = c.getConstructor(String.class, int.class);
User user = (User) con.newInstance("王飽飽", 20);
反射中的類名是字元串,可以採用變數,故更容易修改為不同的資料庫具體產品,且不用在主程式中寫switch方法。
使用DataAccess類,用反射技術,取代IFactory、SqlserverFactory和AccessFactory。
【改進2】反射 + 配置文件
將資料庫名稱放在配置文件中,這樣只需要修改配置文件就可以修改程式的訪問資料庫,不用去改程式代碼。
【總結】從這個角度上說,所有在用簡單工廠的地方,都可以考慮用反射技術來去除switch或if,解除分支判斷帶來的耦合。
12. 狀態模式
狀態模式(State),當一個對象的內在狀態改變時允許改變其行為,這個對象看起來像是改變了其類。
狀態模式主要解決的是當控制一個對象狀態轉換的條件表達式過於複雜時的情況。把狀態的判斷邏輯轉移到表示不同狀態的一系列類當中,可以把複雜的判斷邏輯簡化。
比如,上班時不同時間做不同工作就可以使用狀態模式,首先有一個抽象狀態類,設置多個工作狀態繼承抽象狀態類,有一個工作類(上下文)來負責轉換工作轉態,傳入任意一個工作狀態,當滿足某條件的時候,轉入下一工作狀態。
13. 適配器模式
適配器模式(Adapter),將一個類的介面轉換成客戶希望的另外一個介面。Adapter模式使得原本由於介面不相容而不能一起工作的那些類可以一起工作。
適配器模式有兩種類型,類適配器模式和對象適配器模式。類適配器通過多重繼承對一個介面與另一個介面進行匹配。對象適配器模式主要結構如下:
Target:客戶期待的介面
Adaptee:需要適配的類
Adapter:實現Target介面,通過在內部包裝一個Adaptee對象,把源介面轉換成目標介面
14. 備忘錄模式
備忘錄(Memento):在不破壞封裝性的前提下,捕獲一個對象的內部狀態,併在該對象之外保存這個狀態。這樣以後就可將該對象恢復到原先保存的狀態。
例如,有一個游戲角色,可以在戰鬥之前保存狀態,戰鬥失敗了可以恢復到之前保存的狀態。有
Originator類:發起人(游戲角色)。負責創建一個備忘錄Memento,用以記錄當前時刻它的內部狀態,並可使用備忘錄恢復內部狀態。Originator 可根據需要決定Memento存儲Originator的哪些內部狀態。
Memento類:備忘錄(角色狀態)。負責存儲Originator對象的內部狀態,並可防止Originator 以外的其他對象訪問備忘錄Memento。備忘錄有兩個介面,Caretaker只能看到備忘錄的窄介面,它只能將備忘錄傳遞給其他對象。Originator能夠看到一個寬介面,允許它訪問返回到先前狀態所需的所有數據。
Caretaker類:管理者(負責管理狀態)。負責保存好備忘錄Memento(Originator創建的備忘錄存在Caretaker裡面),不能對備忘錄的內容進行操作或檢查。
Memento模式比較適用於功能比較複雜的,但需要維護或記錄屬性歷史的類,或者需要保存的屬性只是眾多屬性中的一小部分時,Originator可以根據保存的Memento信息還原到前一狀態。
當角色的狀態改變的時候,有可能這個狀態無效,這時候就可以使用暫時存儲起來的備忘錄將狀態複原。
15. 組合模式
組合模式(Composite),將對象組合成樹形結構以表示部分-整體的層次結構。組合模式使得用戶對單個對象和組合對象的使用具有一致性。
比如,一個總公司有財務部、技術部、人力資源部等,還有分公司,分公司同樣有財務部、技術部、人力資源部等。可以先設計一個公司的抽象類(或者介面),裡面有增加、移除、顯示部門、履行職責的抽象方法,然後設計具體的公司類繼承該抽象類,實現這些方法,並加上children相關的方法;部門類也去繼承公司抽象類,實現這些方法。創建分公司的時候同創建總公司相同。
這樣,基本對象可以被組合為更複雜的組合對象,這個組合對象又可以被組合。
16. 迭代器模式
迭代器模式(Iterator),提供一種方法順序訪問一個聚合對象中各個元素,而又不暴露該對象的內部表示。
當你需要訪問一個聚集對象,而且不管這些對象是什麼都需要遍歷的時候,你就應該考慮用迭代器模式。當你需要對聚集有多種方式遍歷時,可以考慮用迭代器模式。Java中的for-each
就是迭代器模式。
迭代器模式就是分離了集合對象的遍歷行為,抽象出一個選代器類來負責,這樣既可以做到不暴露集合的內部結構,又可讓外部代碼透明地訪問集合內部的數據。迭代器模式在訪問數組、集合、列表等數據時,尤其是資料庫數據操作時,是非常普遍的應用,但由於它太普遍了,所以各種高級語言都對它進行了封裝。
17. 單例模式
單例模式(Singleton),保證一個類僅有一個實例,並提供一個訪問它的全局訪問點。
讓類自身負責保存它的唯一實例。這個類可以保證沒有其他實例可以被創建,並且它可以提供一個訪問該實例的方法(靜態方法)。
單例模式有多種實現方法,參考單例模式 | 菜鳥教程
一般採用餓漢式在類直接創建靜態對象:
public class Singleton {
private static Singleton instance = new Singleton();
private Singleton (){}
public static Singleton getInstance() {
return instance;
}
}
如果明確需要使用懶載入,可以採用登記式/靜態內部類方式:
public class Singleton {
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
private Singleton (){}
public static final Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
否則使用雙檢鎖/雙重校驗鎖方式:
public class Singleton {
private volatile static Singleton singleton;
private Singleton (){}
public static Singleton getSingleton() {
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
18. 橋接模式
橋接模式(Bridge),將抽象部分與它的實現部分分離,使它們都可以獨立地變化。(實現指的是抽象類和它的派生類用來實現自己的對象)。
實現系統可能有多角度分類,每一種分類都有可能變化,那麼就把這種多角度分離出來讓它們獨立變化,減少它們之間的耦合。
例如為不同的手機開發游戲,可以將手機硬體和軟體分離,手機有A、B兩個品牌,軟體有M、N兩類。有一個抽象手機類,A手機、B手機繼承抽象手機類;有一個抽象軟體類,M軟體、N軟體繼承抽象軟體類;然後抽象手機類引用抽象軟體類。這樣不管增加手機還是增加軟體都很方便。
合成/聚合復用原則(CARP):儘量使用合成(組合) / 聚合,儘量不要使用類繼承。
19. 命令模式
命令模式(Command),將一個請求封裝為一個對象,從而使你可用不同的請求對客戶進行參數化;對請求排隊或記錄請求日誌,以及支持可撤銷的操作。
例如點燒烤,我們是客戶端,服務員是Invoker,向廚師Receiver發送命令,其中有一個抽象的Command類用於聲明執行操作的介面,ConcreteCommand類繼承Command類,(綁定接收者廚師Receiver)用於實現具體的動作。服務員Invoke是通過Command類來向廚師Receiver進行交互的。
【優點】
- 能較容易地設計一個命令隊列;
- 需要的情況下,可以較容易地將命令記入日誌;
- 允許接收請求的一方決定是否要否決請求。
- 可以容易地實現對請求的撤銷和重做;
- 由於加進新的具體命令類不影響其他的類,因此增加新的具體命令類很容易;
- 命令模式把請求一個操作的對象(服務員)與知道怎麼執行一個操作的對象(廚師)分割開。
敏捷開發原則告訴我們,不要為代碼添加基於猜測的、實際不需要的功能。
如果不清楚一個系統是否需要命令模式,一般就不要著急去實現它,事實上,在需要的時候通過重構實現這個模式並不困難,只有在真正需要如撤銷/恢復操作等功能時,把原來的代碼重構為命令模式才有意義。
20. 職責鏈模式
職責鏈模式(Chain of Responsibility),使多個對象都有機會處理請求,從而避免請求的發送者和接收者之間的耦合關係。將這個對象連成一條鏈,並沿著這條鏈傳遞該請求,直到有一個對象處理它為止。
比如,員工請假,不同管理者級別能處理的請假天數不一樣。設置一個管理者抽象類,有設置上級(繼任者)方法和處理請求的抽象方法,有多個級別的管理者繼承該抽象類,實現處理請求的抽象方法。這樣,員工(客戶端)每次都是向組長請假,組長批不了的就交由部門經理處理,經理處理不了的就由總監處理。
21. 中介者模式
中介者模式(Mediator),用一個中介對象來封裝一系列的對象交互。中介者使各對象不需要顯式地相互引用,從而使其耦合鬆散,而且可以獨立地改變它們之間的交互。
例如,聯合國安理會調節國家衝突。有一個聯合國抽象類,裡面有調節抽象方法(參數為調節信息和調節對象),有具體的安理會繼承聯合國抽象類,實現具體方法;有國家抽象類,需要設置中介者,有具體國家類繼承國家抽象類,實現具體方法;在實際使用時,由中介者去調用國家類的方法。
中介者模式一般應用於一組對象以定義良好但是複雜的方式進行通信的場合,比如剛纔得到的窗體Form對象或Web頁面aspx,以及想定製一個分佈在多個類中的行為,而又不想生成太多的子類的場合。
【優點】
Mediator的出現減少了各個Colleague的耦合,便得可以獨立地改變和復用各個Colleague類和Mediator。
由於把對象如何協作進行了抽象,將中介作為一個獨立的概念並將其封裝在一個對象中,這樣關註的對象就從對象各自本身的行為轉移到它們之間的交互上來,也就是站在一個更巨集觀的角度去看待系統。
【缺點】
- 由於ConcreteMediator 控制了集中化,於是就把交互複雜性變為了中介者的複雜性,這就使得中介者會變得比任何一個ConcreteColleague都複雜。
22. 享元模式
享元模式(Flyweight),運用共用技術有效地支持大量細粒度的對象。
比如為不同人創建類似的網站,可以把網站的公共部分抽取出來,將用戶設置為外部狀態傳進去。這樣的話,就算要為10個用戶創建網站,也可以只有一個網站實例。有用戶類,有一個抽象網站類,裡面有抽象使用方法(傳入用戶),有多個具體的網站類,實現抽象方法;有一個網站工廠負責管理網站(沒有則創建,有則取出返回)。
如果一個應用程式使用了大量的對象,而大量的這些對象造成了很大的存儲開銷時就應該考慮使用;還有就是對象的大多數狀態可以外部狀態,如果刪除對象的外部狀態,那麼可以用相對較少的共用對象取代很多組對象,此時可以考慮使用享元模式。
【優點】
- 享元模式的優點在於它可以極大減少記憶體中對象的數量,使得相同對象或相似對象在記憶體中只保存一份。
- 享元模式的外部狀態相對獨立,而且不會影響其內部狀態,從而使得享元對象可以在不同的環境中被共用。
【缺點】
- 享元模式使得系統更加複雜,需要分離出內部狀態和外部狀態,這使得程式的邏輯複雜化。
- 為了使對象可以共用,享元模式需要將享元對象的狀態外部化,而讀取外部狀態使得運行時間變長。
Java中的字元串(會放到字元串池中)就是用了享元模式。
23. 解釋器模式
解釋器模式(interpreter),給定一個語言,定義它的文法的一種表示,並定義一個解釋器,這個解釋器使用該表示來解釋語言中的句子。
比如正則表達式,可以用特定的文法去匹配字元串。
AbstractExpression類:抽象表達式。聲明一個抽象的解釋操作,這個介面為抽象語法樹中所有的節點所共用。
TerminalExpression類:終結符表達式。實現與文法中的終結符相關聯的解釋操作。實現抽象表達式中所要求的介面,主要是一個interpret() 方法。文法中每一個終結符都有一個具體終結表達式與之相對應。
NonterminalExpression類:非終結符表達式,為文法中的非終結符實現解釋操作。對文法中每一條規則 R1、R2……Rn 都需要一個具體的非終結符表達式類。通過實現抽象表達式的interpret() 方法實現解釋操作。解釋操作以遞歸方式調用上面所提到的代表R1、R2……Rn中各個符號的實例變數。
Context類:包含解釋器之外的一些全局信息。
24. 訪問者模式
訪問者模式(Visitor),表示一個作用於某對象結構中的各元素的操作。它使你可以在不改變各元素的類的前提下定義作用於這些元素的新操作。
比如,有男人、女人2種數據結構,每類人都有多個情緒,情緒對應的操作不同。有抽象訪問者類Visitor,為該對象結構中ConcreteElement的每一個類聲明一個Visit操作;ConcreteVisitor1和ConcreteVisitor2類,具體訪問者,實現每個由Visitor聲明的操作;人抽象類Element,定義一個Accept操作,它以一個訪問者為參數;男人類 ConcreteElementA和女人類ConcreteElementB,具體元素,實現Accept操作;人的集合ObjectStructure類,能枚舉它的元素,可以提供一個高層的介面以允許訪問者訪問它的元素。
Accept操作充分利用雙分派技術,實現處理與數據結構的分離。
在客戶程式中將具體狀態作為參數傳遞給男人類完成了一次分派,然後男人類調用作為參數的具體狀態中的方法男人反應,同時將自己(this)作為參數傳遞進去。這便完成了第二次分派。雙分派意味著得到執行的操作決定於請求的種類和兩個接收者的類型。接受方法就是一個雙分派的操作,它得到執行的操作不僅決定於狀態類的具體狀態,還決定於它訪問的人的類別。
訪問者模式適用於數據結構相對穩定的系統,它把數據結構和作用於結構上的操作之間的耦合解脫開,使得操作集合可以相對自由地演化。
【優點】
- 增加新的操作很容易,因為增加新的操作就意味著增加一個新的訪問者。訪問者模式將有關的行為集中到一個訪問者對象中。
【缺點】
- 增加新的數據結構變得困難。
訪問者模式的目的是要把處理從數據結構分離出來。很多系統可以按照演算法和數據結構分開,如果這樣的系統有比較穩定的數據結構,又有易於變化的演算法的話,使用訪問者模式就是比較合適的,因為訪問者模式使得演算法操作的增加變得容易。反之,如果這樣的系統的數據結構對象易於變化,經常要有新的數據對象增加進來,就不適合使用訪問者模式。
設計模式總結[23種]
1. 創建型模式
創建型模式 --> 對象怎麼來
抽象工廠模式:提供一個創建一系列或相關依賴對象的介面,而無需指定它們具體的類。
建造者模式:將一個複雜對象的構建與它的表示分離,使得同樣的構建過程可以創建不同的表示。
工廠方法模式:定義一個用於創建對象的介面,讓子類決定實例化哪一個類,工廠模式使一個類的實例化延遲到其子類。
原型模式:用原型實例指定創建對象的種類,並且通過拷貝這些原型創建新的對象。
單例模式:保證一個類僅有一個實例,並提供一個訪問它的全局訪問點。
創建型模式隱藏了這些類的實例是如何被創建和放在一起,整個系統關於這些對象所知道的是由抽象類所定義的介面。這樣,創建型模式在創建了什麼、誰創建它、它是怎麼被創建的,以及何時創建這些方面提供了很大的靈活性。
2. 結構型模式
結構型模式 --> 對象和誰有關
適配器模式:將一個類的介面轉換成客戶希望的另外一個介面。適配器模式使得原本由於介面不相容而不能一起工作的那些類可以一起工作。
橋接模式:將抽象部分與它的實現部分分離,使它們都可以獨立地變化。
組合模式:將對象組合成樹形結構以表示“部分-整體”的層次結構,組合模式使得用戶對單個對象和組合對象的使用具有一致性。
裝飾器模式:動態地給一個對象添加一些額外的職責。就增加功能來說,裝飾模式相比生成子類更加靈活。
外觀模式:為子系統中的一組介面提供一個一致的界面,外觀模式定義了一個高層介面,這個介面使得這一子系統更加容易使用。
享元模式:運用共用技術有效地支持大量細粒度的對象。
代理模式:為其他對象提供一種代理以控制對這個對象的訪問。
3. 行為型模式
行為型模式 --> 對象與對象在幹嘛
觀察者模式:定義對象間的一種一對多的依賴關係,當一個對象的狀態發生改變時,所有依賴於它的對象都得到通知並被自動更新。
模板方法模式:定義一個操作的演算法骨架,而將一些步驟延遲到子類中,模板方法使得子類可以不改變一個演算法的結構即可重定義該演算法的某些特定步驟。
命令模式:將一個請求封裝為一個對象,從而使你可用不同的請求對客戶進行參數化;可以對請求排隊或記錄請求日誌,以及支持可撤銷的操作。
狀態模式:允許一個對象在其內部狀態改變時改變它的行為,讓對象看起來似乎修改了它的類。
職責鏈模式:多個對象都有機會處理請求,從而避免請求的發送者和接收者之間的耦合關係。將這些對象連成一條鏈,並沿著這條鏈傳遞該請求,直到有一個對象處理它為止。
解釋器模式:給定一個語言,定義它的文法的一種表示,並定義一個解釋器,這個解釋器使用該表示來解釋語言中的句子。
中介者模式:一個中介對象來封裝一系列的對象交互。中介者使各對像不需要顯式地相互引用,從而使其耦合鬆散,而且可以獨立地改變它們之間的交互。
訪問者模式:表示一個作用於某對象結構中的各元素的操作。它使你可以在不改變各元素的類的前提下定義作用於這些元素的新操作。
策略模式:定義一系列的演算法,把它們一個個封裝起來,並且使它們可相互替換,使得演算法可獨立於使用它的客戶而變化。
備忘錄模式:在不破壞封裝性的前提下,捕獲一個對象的內部狀態,併在該對象之外保存這個狀態。這樣以後就可將該對象恢復到原先保存的狀態。
迭代器模式:提供一種方法順序訪問一個聚合對象中各個元素,而又不需暴露該對象的內部表示。