設計模式
23個設計模式,23個意圖
1. 設計模式概要
設計模式的核心在于提供了相關問題的解決方案,使得人們可以更加簡單方便的復用成功的設計和體系結構
設計模式的類別
創建型 | 結構型 | 行為型 | |
---|---|---|---|
類 | 工廠方法模式 | 適配器模式(類) | 解釋器模式 模版方法模式 |
對象 | 抽象工廠模式 生成器模式 原型模式 單例模式 | 適配器模式(對象) 橋接模式 組合模式 裝飾模式 外觀模式 享元模式 代理模式 | 責任鏈模式 命令模式 迭代器模式 終結者模式 備忘錄模式 觀察者模式 狀態模式 策略模式 訪問者模式 |
2. 創建型設計模式
2.1 工廠模式(簡單工廠模式)
屬于創建型但不屬于23中設計模式之一
意圖:
定義一個工廠類,他可以根據參數的不同返回不同類的實例,被創建的實例通常都具有共同的父類
違背了開閉原則
2.2 工廠方法模式(Factory Method)
意圖:
定義一個用于創建對象的接口,讓子類決定實例化哪一個類。工廠方法模式使一個類的實例延遲到其子類
主要角色:
- 抽象工廠:提供了創建產品的接口,調用者通過它訪問具體工廠的工廠方法來創建產品。(體現了多態)
- 具體工廠:主要是實現抽象工廠中的抽象方法,完成具體產品的創建
- 抽象產品:定義了產品的規范,描述了產品的主要特性和功能
- 具體產品:實現了抽象產品角色所定義的接口,由具體工廠來創建,同具體工廠一一對應
比如:
咖啡工廠接口(抽象工廠):有創建咖啡對象的方法。其下有兩個具體工廠(美式咖啡工廠和拿鐵咖啡工廠),他們去實現父類的接口,重寫父類的方法。由具體工廠生成具體產品,再由具體產品(美式咖啡和拿鐵咖啡)實現抽象產品(咖啡)
由此要增加產品類時相應地增加工廠類,不需要修改工廠類的代碼了
注:interface接口 對應 implements實現
優點:
- 用戶只需要知道具體工廠的名稱就可以得到所要的產品,無需知道產品的具體創建過程
- 在系統增加新的產品時只需要添加具體產品類和對應的具體工廠類,無需對原工廠進行任何修改,滿足開閉原則(對擴展開放,對修改關閉)
缺點:
- 每增加一個產品就要增加一個具體產品類和一個對應的具體工廠類,增加了系統的復雜度
題目描述:
- 當一個類不知道它所必須創建的對象的類的時候
- 當一個類希望由它的子類來指定它所創建的對象的時候
- 當類將創建對象的職責委托各多個幫助子類中的某一個,并且你希望將哪一個幫助子類時代理這一信息局部化的時候
2.3 抽象工廠模式
意圖:
提供一個創建一系列相關或相互依賴對象的接口,且訪問類無需指定它們具體的類
抽象工廠模式是工廠方法模式的升級版,工廠方法模式只能生產一個等級的產品,而抽象工廠模式可以生成多個等級的產品
主要角色:
- 抽象工廠:提供了創建產品的接口,它包含多個創建產品的方法,可以創建多個不同等級的產品
- 具體工廠:主要是實現抽象工廠中的抽象方法,完成具體產品的創建
- 抽象產品:定義了產品的規范,描述了產品的主要特性和功能,抽象工廠有多個抽象產品
- 具體產品:實現了抽象產品角色所定義的接口,由具體工廠來創建,同具體工廠一一對應
比如:
咖啡店不僅要生成咖啡還要生成甜品,比如提拉米蘇、抹茶慕斯等,按照工廠方法模式,需要定義提拉米蘇類、提拉米蘇方法、抹茶慕斯類、抹茶慕斯方法,容易發生類爆炸情況。抽象工廠中,拿鐵咖啡、美式咖啡是一個產品等級(咖啡),提拉米蘇、抹茶慕斯是一個產品等級(甜點),拿鐵咖啡、提拉米蘇是同意產品族(意大利風味),美式咖啡、抹茶慕斯是同一產品族(美式風味)
即抽象工廠接口下有兩個具體工廠(意大利風味和美式風味),兩個具體工廠生成其下相應地具體產品,再由具體產品去實現抽象產品(咖啡和甜點)
優點:
- 當一個產品族中的多個對象被設計成一起工作時,它能保證客戶端始終只使用同一個產品族中的對象
缺點:
- 當產品族中需要增加一個新的產品時,所有的工廠都需要修改
題目描述:
- 提供一系列相關或相互依賴的對象的接口,而無需指定這些對象所屬的具體類
- 當一個系統應該獨立于它的產品創建、組合和表示時
- 當要強調一系列相關的產品對象的設計以便進行聯合使用時
- 一個系統要由多個產品系列中的一個來配置時
- 為圖形用戶界面(GUI)組件定義不同平臺的并行類層次結構
使用場景:
- 當需要創建的對象是已一系列相互關聯或相互依賴的產品族時,如:電器工廠中的電視機、洗衣機、空調等
- 系統中有多個產品族,但每次只使用其中的某一族產品。如:有人只喜歡穿某一個品牌的衣服和鞋
- 系統中提供了產品的類庫,全且所有產品的接口相同,客戶端不依賴產品實例的創建細節和內部結構
如:輸入發換皮膚,一整套一起換,生成不同操作系統 程序
2.4 生成器模式(建造者模式)
意圖:
將一個復雜對象的構建與它的表示分離,使得同樣的構建過程可以創建不同的表示
主要角色:
- 抽象建造者類:這個接口規定要實現對象的那些部分的創建,并不涉及具體部件對象的創建
- 具體建造者類:實現Builder接口,完成復雜產品的各個部件的具體創建方法,在構造過程完成后,提供產品的實例
- 產品類:要創建的負責對象
- 指揮者類:調用具體建造者來創建復雜對象的各個部分,在指導者中不涉及具體產品的信息,只負責保證對象各部分完整創建或按某種順序創建
比如:
分離了部件的構造(由Builder來負責)和裝配(Director負責),從而可以構造出負責的對象,這個模式使用于某個對象的構建過程復雜的情況
由于實現了構建和裝配的解耦,不同的構建器,相同的裝配,也可以做出不同的對象;相同的構建器,不同的裝配順序也可以做出不同的對象。也就是實現了構建算法、裝配算法的解耦,實現了更好的復用
建造者模式可以將部件和其組裝過程分開,一步一步創建一個復雜的對象,用戶只需要指定復雜對象的類型就可以得到該對象,而無需知道其內部的具體構造細節
優點:
- 封裝性很好
- 客戶端不必知道產品內部組成的細節
- 可以更加精細地控制產品的創建過程
- 容易進行擴展
缺點:
- 建造者模式所創建的產品一般具有較多的共同點,其組成部分相似,如果產品之間的差異性很大,則不適合使用建造者模式,因此其使用范圍受到一定的限制
題目描述:
- 當創建復雜對象的算法應該獨立于該對象的組成部分及其裝配方式時
- 當構造過程必須允許被構造的對象有不同的表示時
- 將一個復雜對象的構建與其表示分離,使得同樣的構建過程可以創建不同的表示
- 適用于抽象復雜對象的構建步驟和基于構建過程的具體實現構建復雜對象的不同表示
- 關鍵字:復雜對象,不同的表示
使用場景:
建造者模式創建的是復雜對象,其產品的各個部分經常面臨著劇烈的變化,但將它們組合在一起的算法卻相對穩定,所以它通常在以下場合使用
- 創建的對象較復雜,由多個部件構成,個部件面臨著復雜的變化,但構件間的建造順序是穩定的
- 產品的構建過程和最終表示是獨立的
2.5 原型模式
意圖:
用原型實例指定創建對象的種類,并且通過復制這些原型創建新的對象(用一個已經創建的實例作為原型,通過復制該原型對象來創建一個和原型對象相同的新對象
主要角色:
- 抽象原型類:規定了具體原型對象必須實現的clone()方法
- 具體原型類:實現抽象原型類的clone()方法,它是可被復制的對象
- 訪問類:使用具體原型類中的clone()方法來復制新的對象
比如:
同一學校的”三好學生“獎狀除了獲獎人姓名不同,其他相同,可以使用原型模式復制多個”三好學生“獎狀出來,然后修改獎狀上的名字即可
即由抽象原型類下的具體原型類重寫父類接口中的clone,由訪問類復制對象
題目描述:
- 應該類的實例只能有幾個不同狀態組合中的一種
- 當一個系統應該獨立于它的產品創建、構成和表示時
- 當要實例化的類是在運行時刻指定是,例如:通過動態裝載
- 為了避免創建一個與產品類層次平行的工廠類層次時
- 當一個類的實例只能有幾個不同狀態組合中的一種時,建立相應數目的原型并克隆他們,可能比每次用合適的狀態手工實例化該類更方便些
使用場景:
- 對象的創建非常復雜,可以使用原型模式快捷的創建對象
- 性能和安全要求比較高
2.6 單例模式(選擇題)
意圖:
保證一個類僅有一個實例,并提供一個訪問它的全局訪問點
題目描述:
-
它能夠保證一個類只產生唯一的一個實例
-
一個類僅有一個實例
-
當類只能有一個實例而且客戶可以從一個眾所周知的訪問點訪問它時
-
當這個唯一實例應該是通過之類化可擴展的,并且客戶無需更改代碼就能使用一個擴展的實例時
2.7 創建型設計模式總結
創建型設計模式 | 定義 | 關鍵字 |
---|---|---|
工廠方法模式(Factory Method) | 定義一個用于創建對象的接口,讓子類決定實例化哪一個類。工廠方法模式使一個類的實例延遲到其子類 | 子類決定實例化 |
抽象工廠模式(Abstract Factory) | 提供一個創建一系列相關或相互依賴對象的接口,且訪問類無需指定它們具體的類 | 抽象接口 |
構建者模式(生成器模式)(Builder) | 將一個復雜對象的構建與它的表示分離,使得同樣的構建過程可以創建不同的表示 | 類和構造分離 |
原型模式(Prototype) | 用原型實例指定創建對象的種類,并且通過復制這些原型創建新的對象 | 原型實例,拷貝 |
單例模式(Singleton) | 保證一個類僅有一個實例,并提供一個訪問它的全局訪問點 | 唯一實例 |
3. 結構型設計模式
3.1 適配器模式
分為類和對象型,類適配器模式使用繼承,類之間的耦合度比對象型高,且要求程序員了解現有組件庫中的相關組件的內部結構,所以應用相對較少;對象適配器模式使用聚合、組合
意圖:
將一個類的接口轉換成客戶希望的另外一個接口,適配器模式使得原本由于接口不兼容而不能一起工作的那些類可以一起工作
主要角色:
- 目標接口:當前系統業務所期待的接口,它可以是抽象類或接口
- 適配者類:它是被訪問和適配的現存組件庫中的組件接口
- 適配器類:它是一個轉換器,通過繼承或引用適配者的對象,把適配者接口轉換成目標接口,讓客戶按目標接口的格式訪問適配者
比如:
類型:定義一個適配器來實現當前系統的業務接口,同時又繼承現有組件庫中已經存在的組件
對象型:將現有組件庫中已經實現的組件引入適配器類中,該類同時實現當前系統的業務接口
現有一臺電腦只能讀SD卡。而要讀取TF卡中的內容就需要使用適配器模式,創建一個讀卡器,將TF卡中的內容讀取出來注:類適配器模式違背了合成復用原則,類適配器是客戶類有一個接口規范的情況下可用,反之不可用
題目描述:
- 將一個對象加以包裝以給客戶提供其希望的另外一個接口
- 想使用一個已經存在的類,而其接口不符合要求
- 使所有接口不兼容類可以一起工作
- 將一個類的接口轉換成客戶希望的另一個接口
- 創建一個可以復用的類,該類可以與其他不相關的類或不可預見的兩位協同工作
- 想使用一個已經存在的子類,但不可能對每一個都進行子類化以匹配他們的接口,對象適配器可以適配它的父類接口
3.2 橋接模式
意圖:
將抽象部分與其實現部分分離,使他們都可以獨立地變化。它是用組合關系代替繼承關系來事項,從而降低了抽象和實現這兩個可變維度的耦合度
主要角色:
- 抽象化角色:定義抽象類,并包含一個對實現化對象的引用
- 擴展抽象化角色:是抽象化角色的子類,實現父類中的業務方法,并通過組合關系調用實現化角色中的業務方法,并通過組合關系調用實現化角色中的業務方法
- 實現化角色:定義實現化角色的接口,供擴展抽象化角色調用
- 具體實現化角色:給出實現化角色接口的具體實現
比如:
開發一個跨平臺視頻播放器,可以在不同操作系統平臺(Windows、Mac、Linux)上播放多種格式的視頻文件,常見的視頻格式包括RMVB、AVI、WMV等。該播放器包含了兩個維度,適合使用橋接模式。橋接模式提高了系統的可擴充性,在兩個維度,都不需要修改原有系統。
如:如果還有一種視頻文件類型wmv,我們只需要再定義一個類實現VideoFile接口即可
優點:
- 提高了系統的可擴擴充性
- 實現細節對客戶透明
題目描述:
- 類的抽象和其實現之間不希望有一個固定的綁定關系
- 不希望在抽象和它的實現部分之間有一個固定的綁定關系
- 都給另一個對象提供了一定程度上的間接性,都涉及從自身以外的一個接口向這個對象轉發請求
- 類的抽象以及它的實現都應該可以通過生成子類的方法加以擴充
- 對一個抽象的實現部分的修改應對客戶不產生影響,即客戶代碼不必重新編譯
- 想對客戶完全隱藏抽象的實現部分
- 有許多類要生成的類層次結構
- 想在多個對象間共享實現,但同時要求客戶并不知道這一點
使用場景:
- 當一個類存在兩個獨立變化的維度,且兩個維度都需要進行擴展時
- 當一個系統不希望使用繼承或因為多層次繼承導致系統類的個數急劇增加時
- 當一個系統需要再構建的抽象化角色和具體化角色之間增加更加的靈活性時,避免在兩個層次之間建立靜態的繼承聯系,通過橋接模式可以使他們在抽象層建立一個關聯關系
3.3 組合模式
意圖:
將對象組合成樹形結構以表示“部分-整體”的層次結構,組合模式使得用戶對單個對象和組合對象的使用具有一致性
主要角色:
- 抽象根節點:定義系統各個層次對象的共有方法和屬性,可以預先定義一些默認行為和屬性
- 樹枝節點:定義樹枝節點的行為,存儲子節點,組合樹枝節點和葉子節點形成一個樹形結構
- 葉子節點:葉子節點對象,其下再無分支,是系統層次遍歷的最小單位
比如:
樹形結構的文件夾系統
優點:
- 組合模式可以清楚地定義分層次的復雜對象,表示對象的全部或部分分層次,它讓客戶忽略了層次的差異,方便對整個層次結構進行控制
- 客戶端可以一致地使用一個組合結構或其中單個對象,不必關心處理的是單個對象還是整個組合結構,簡化了客戶端代碼
- 在組合模式中增加新的樹枝節點和葉子節點都很方便,無需對現有類庫進行任何修改,符合“開閉原則”
- 組合模式為樹形結構的面向對象實現提供了一種靈活的解決方案,通過葉子節點和樹枝節點的遞歸組合,可以形成復雜的樹形結構,但對樹形結構的控制卻非常簡單
題目描述:
- 表示對象的部分-整體層次結構
組合模式正是應樹形結構而生,所以組合模式出現在樹形結構的地方,比如:文件目錄顯示,多級目錄呈現等樹形結構數據的操作
3.4 裝飾器模式
意圖:
動態地給一個對象添加一些額外的職責,就增加功能而言,裝飾器模式比生成子類更加靈活
指在不改變現有對象結構的情況下,動態地給該對象增加一些職責(即增加其額外功能)的模式
主要角色:
- 抽象構件角色:定義一個抽象接口以規范準備接收附加責任的對象
- 具體構件角色:實現抽象構件,通過裝飾角色為其添加一些職責
- 抽象裝飾角色:繼承或實現抽象構件,并包含具體構件的實例,可以通過其子類擴展具體構件的功能
- 具體裝飾角色:實現抽象裝飾的相關方法,并給具體構件對象添加附加的責任
題目描述:
- 將一個對象加以包裝以提供一些額外的行為
- 在不影響其他對象的情況下,以動態、透明的方式給單個對象添加職責
- 動態地給一個對象添加一些額外的職責
- 處理那些可以撤銷的職責
- 當不能采用生成子類的方式進行擴充時。一種情況是,可能有大量獨立地擴展,為支持每一種組合將產生大量的子類,使得子類數目呈爆炸性增長。另一種情況可能是,由于類定義被隱藏,或類定義不能用于生成子類。
3.5 外觀模式
意圖:
為子系統中的一組接口提供一個一致的界面,外觀模式定義的一個高層接口,這個接口是使得這一子系統更加任意使用
主要角色:
- 外觀角色:為多個子系統對外提供一個共同的接口
- 子系統角色:實現系統的部分功能,客戶可以通過外觀角色訪問它
比如:
智能家電控制:
將打開燈、打開電視、打開空調、關燈、關電視、關空調等操作通過智能音箱語音控制
優點:
- 降低了子系統與客戶端之間的耦合度,使得子系統的變化不會影響用它的客戶類
- 對客戶屏蔽了子系統組件,減少了客戶處理的對象數目,并使得子系統使用起來更加容易
缺點:
- 不符合開閉原則,修改麻煩
題目描述:
- 將一系列對象加以包裝以簡化其接口
- 需要為一個復雜子系統提供一個簡單接口
- 客戶程序與抽象類的實現部分之間存在很大的依賴性,引入外觀模式將這個子系統與客戶以及其他的子系統分離,可以提高子系統的獨立性和可移植性
- 當需要構建一個層次結構的子系統時,使用外觀模式定義子系統中每層的入口點,如果子系統之間是相互依賴的,則可以讓他們僅通過外觀模式進行通信,從而簡化了他們之間的依賴性
使用場景:
- 對分層結構系統構建時,使用外觀模式定義子系統中每層的入口點可以簡化子系統之間的依賴關系
3.6 享元模式
意圖:
運用共享技術有效地支持大量細粒度的對象,從而提高系統資源的利用率
享元模式中存在兩種狀態:
- 內部狀態,不會隨環境的改變而改變的可共享部分
- 外部狀態,隨環境改變而改變的不可以共享的部分
主要角色:
- 抽象享元角色
- 具體享元角色:為每一個具體享元提供唯一的享元對象
- 非享元角色
- 享元工廠角色
比如:
俄羅斯方塊:
有不同的顏色和形狀(不同顏色為外部狀態)
題目描述:
- 因使用大量的對象而造成很大的存儲開銷時,進行對象共享,以減少對象數量從而達到較少的內存占用并提升性能
- 一個應用程序使用了大量的對象
- 完全由于使用大量的對象,造成很的的存儲開銷
- 對象的大對數狀態都可變成為外部狀態
- 如果刪除對象的外部狀態,那么可以用相對較少的共享對象取代很多組對象
3.7 代理模式
意圖:
為其他對象提供一種代理以控制對這個對象的訪問
由于某些原因需要給某對象提供一個代理以控制對該對象的訪問,這時,訪問對象不適合或者不能直接引用目標對象,代理對象作為訪問對象和目標對象之間的中介
主要角色:
- 抽象主題類:通過接口或則抽象類聲明真實主題和代理對象實現的業務方法
- 真實主題類:實現了抽象主題中的具體業務,是代理對象所代表的真實對象,是最終要引用的對象
- 代理類:提供了與真實主題相同的接口,其內部含有對真實主題的引用,它可以訪問、控制、或擴展真實主題的功能
比如:
火車站買票:
如果要買火車票的話需要去火車站買票,坐車到火車站,排隊等一系列的操作,顯然比較麻煩,而火車站在多個地方都有售票點,我們去代售點買票就方便很多
優點:
- 代理模式在客戶端和目標對象之間起到一個中介作用和保護目標對象的作用
- 代理對象可以擴展目標對象的功能
- 代理模式能將客戶端與目標對象分離,在一定程度上降低了系統的耦合度
缺點:
- 增加了系統的復雜度
題目描述:
-
將一個對象加以包裝以控制對這個兌現的訪問
-
在需要比較通用和復雜的對象指針代替簡單的指針時
-
遠程代理,為一個對象在不同地址空間提供局部代表
將網絡通信部分隱藏起來,只暴露給本地服務一個接口,通過該接口即可訪問遠程服務提供的功能,而不必過多關心通信部分的細節
-
虛代理,根據需要創建開銷很大的對象
-
保護代理,控制對原始對象的訪問,用于對象應該有不同的訪問權限的時候
可以給不同的用戶提供不同級別的使用權限
-
智能引用,取代了簡單指針,它在訪問對象時執行一些附加操作
-
-
為其他對象提供一個代理以控制對這個對象的訪問
-
防火墻代理,當你將瀏覽器配置成使用代理模式時,防火墻就將你的瀏覽器的請求轉給互聯網,當互聯網返回響應時,代理服務器再把他轉給你的瀏覽器
3.8 結構型設計模式總結
結構 | 定義 | 關鍵字 |
---|---|---|
適配器模式(Adapter) | 將一個類的接口轉換成客戶希望的另外一個接口,使得原本不相容的接口得以協同工作 | 轉換,兼容接口 |
橋接模式(Bridge) | 將抽象部分與其實現部分分離,使他們都可以獨立地變化 | 抽象和實現分離 |
組合模式(Composite) | 將對象組合成樹形結構以表示“部分-整體”的層次結構,組合模式使得用戶對單個對象和組合對象的使用具有一致性 | 整體-部分,樹形結構 |
裝飾器模式(Decorator) | 動態地給一個對象添加一些額外的職責,就增加功能而言,裝飾器模式比生成子類更加靈活 | 附加職責 |
外觀模式(Facade) | 為子系統中的一組接口提供一個一致的界面,外觀模式定義的一個高層接口,這個接口是使得這一子系統更加任意使用 | 對外同一接口 |
享元模式(Flyweight) | 運用共享技術有效地支持大量細粒度的對象 | 細粒度,共享 |
代理模式(Proxy) | 為其他對象提供一種代理以控制對這個對象的訪問 | 代理控制 |
4. 行為型設計模式
4.1 責任鏈模式
意圖:
使多個對象都有機會處理請求,從而避免請求的發送者和接受者之間的耦合關系。將這些對象連成一條鏈,并沿著這條鏈傳遞該請求,直到有一個對象處理它為止
主要角色:
- 抽象處理者角色:定義一個處理請求的接口,包含抽象處理方法和一個后繼連接
- 具體處理者角色:實現抽象處理者的處理方法,判斷能否處理本次請求,如果可以處理請求則處理,否則將該請求轉給它的后繼者
- 客戶類角色:創建處理鏈,并向鏈頭的具體處理者對象提交請求,它不關心處理細節和請求的傳遞過程
比如:
學生請假,不同的批假天數,找不同的上級領導
優點:
- 降低了對象之間的耦合度
- 增強了系統的可擴展性,可以增加請求處理類,滿足開閉原則
- 增強了給對象指派職責的靈活性,當工作流程發生變化,可以動態地改變鏈內的成員或者修改他們的次序,也可動態地新增或者刪除責任
- 責任鏈簡化了對象之間的連接
- 責任分擔,每個類只需要處理自己該處理的工作,不能處理的傳遞給下一個對象完成
缺點:
- 不能保證每個請求一定被處理
- 對比較長的責任鏈,請求的處理可能涉及多個處理對象,系統性能將受到一定影響
- 責任鏈建立的合理性要靠客戶端來保證,增加了客戶端的復雜性,可能會由于責任鏈的錯誤設置而導致系統出錯
題目描述:
- 有多個對象可以處理一個請求,在運行時刻自動確定哪個對象處理
- 一個客戶需要使用一組相關對象
- 想在不明確指定接受者的情況下想多個對象中的一個提交一個請求
- 可處理一個請求的對象集合應被動態指定
4.2 命令模式
意圖:
將一個請求封裝為一個對象,從而使得可以用不同的請求對客戶進行參數化,對請求排隊或記錄請求日志,以及支持可撤銷的操作
將一個請求封裝為一個對象,是發出請求的責任和執行請求的責任分割開,這樣兩者之間通過命令對象進行溝通,這樣方便將命令對象進行存儲、傳遞、調用、增加和管理。
主要角色:
- 抽象命令類角色:定義命令接口,聲明執行的方法
- 具體命令角色:具體的命令,實現命令的接口,通常會持有接收者,并調用接收者的功能來完成命令要執行的操作
- 實現者/接收者角色:接收者,真正執行命令的對象。任何類都可能成為一個接收者,只要它能夠實現命令要求實現的相應功能
- 調用者/請求者角色:要求命令執行相應操作的地方,也就是說相當于使用命令對象的入口
比如:
吃飯點餐,顧客包訂單交給服務員,服務員把訂單放在訂單柜說“訂單來了”,廚師根據訂單準備餐點
服務員:調用者角色,由他發起命令
廚師:接收者角色,真正命令執行的對象
訂單:命令中包含訂單
優點:
- 降低系統的耦合度
- 增加或刪除命令非常方便
- 可以實現宏命令
缺點:
- 可能會導致某些系統有過多的具體命令類
- 系統結構更加復雜
題目描述:
- 抽象出執行的動作以參數化某對象
- 將請求封裝為對象從而可以使用不同的請求對客戶進行參數化
- 在不同的時刻指定、排列和執行請求
- 支持修改日志這樣當系統崩潰時,這些修改可以被重做一遍
使用場景:
- 需要將請求調用者和請求接收者解耦,使得調用者和接收者不直接交互
- 系統需要支持命令的撤銷(undo)操作和恢復(redo)操作
- 系統需要再不同的時間指定請求、將請求排隊和執行請求
4.3 解釋器模式(沒怎么考)
意圖:
給定一個語言,定義他的文法的一種表示,并定義一個解釋器,這個解釋器使用該表示來解釋語言中的句子
比如:
加減法運算,規則為:由數值和±符號組成的合法序列
題目描述:
- 有一個語言需要解釋執行,并且可將句子表示為一個抽象語法樹
- 當有一個語言需要解釋執行,并且可將該語言中的句子表示為一個抽象語法樹時
4.4 迭代器模式
意圖:
提供一種方法順序訪問一個聚合對象中的各個元素,且不需要暴露該對象的內部表示
主要角色:
- 抽象聚合角色:定義存儲、添加、刪除聚合元素以及創建迭代器對象的接口
- 具體聚合角色:實現抽象聚合類,返回一個具體迭代器的實例
- 抽象迭代器角色:定義訪問和遍歷聚合元素的接口,通常包含hasNext()、next()等方法
- 具體迭代器角色:實現抽象迭代器接口中定義的方法,完成聚合對象的遍歷,記錄遍歷的當前位置
題目描述:
- 訪問一個聚合對象的內容而無需暴露它的內部細節的表示
- 支持對聚合對象的多種遍歷
- 為遍歷不同的聚合結構提供一個統一的接口
4.5 中介者模式
意圖:
用一個中介對象來封裝一系列的對象交互,中介者使各個對象不需要顯式的互相引用,從而使其耦合松散,而且可以獨立地改變他們之間的交互
比如:
同事類之間的關系比較復雜,是一個網狀結構,而引入中介者模式,那么同事類之間的關系將變為星型結構
主要角色:
- 抽象中介角色:它是中介者的接口,提供了同事對象注冊與轉發同事對象信息的抽象方法
- 具體中介者角色:實現中介者接口,定義一個List來管理同事對象,協調各個同事角色之間的交互關系,因此它依賴于同事角色
- 抽象同事類角色:定義同事類的接口,保存中介者對象,提供同事對象交互的抽象方法,實現所有相互影響的同事類的公共功能
- 具體同事類角色:是抽象同事類的實現者,當需要與其他同事對象交互時,由中介者對象負責后續的交互
比如:
房屋中介,房主將房屋托管給房屋中介,而租房者從房屋中介獲取房屋信息
優點:
- 松散耦合
- 集中控制交互
- 一對多關聯變成一對一關聯
缺點:
- 當同事類太多時,中介者的職責將很大,它會變復雜而龐大,以至于系統難以維護
題目描述:
- 一個對象引用其他很多對象并且直接與這些對象通信,導致難以復用該對象
- 一組對象以定義良好但是復雜的方式進行通信,產生的相互依賴關系結構混亂且難以理解
- 減少多個對象或類之間的通信復雜性
- 中介模式和觀察者模式是相互競爭的模式,差別:前者的中介對象封裝了其他對象間的通信,而后者通過引入其他對象來分布通信
- 欲使一個后端數據模型能夠被多個前端用戶界面連接
4.6 備忘錄模式
意圖:
在不破壞封裝性的前提下捕獲一個對象的內部狀態,并在對象之外保存這個狀態。這樣以后就可以將對象恢復到原先保存的狀態
優點:
- 提供了一種可以恢復狀態的機制,當用戶需要時能夠比較方便地將數據恢復到某個歷史的狀態
- 實現了內部狀態的封裝,除了創建它的發起人之外,其他對象都不能訪問這些狀態信息
- 簡化了發起人類,發起人不需要管理和保存其內部狀態的各個備份,所有狀態信息都保存在備忘錄中,并由管理者進行管理,這符合單一職責原則
缺點:
- 資源消耗大,如果要保存的內部狀態信息過多或者特別頻繁,將會占用較大的內存資源
題目描述:
- 將對象的狀態恢復到先前的狀態
- 在不破壞封裝性的前提下,捕獲對象的內部狀態并在對象之外保存
- 必須保存一個對象在某一個時刻的(部分)狀態
- 如果一個用接口來讓其他對象之間得到這些狀態,將會暴露對象的實現細節并破壞對象的封裝性
4.7 觀察者模式
意圖:
定義對象間的一種一對多的依賴關系,當一個對象的狀態發生改變時,所有依賴于它的對象都得到通知并被自動更新
比如:
使用微信公眾號,公眾號有內容更新的話,會推送給關注公眾號的微信用戶端
題目描述:
- 當一個對象必須通知其他對象,而它又不能假定其他對象時誰時
- 在發布-訂閱消息模型中,訂閱者訂閱一個主題后,當該主題有新消息到達時,所要訂閱者都會受到通知
- 使所要交互的對象盡量松耦合
- 當一個對象的改變需要改變其它對象
4.8 狀態模式
意圖:
允許一個對象在其內部狀態改變時改變它的行為。對象看起來似乎修改了它的類
對有狀態的對象,把復雜的“判斷邏輯”提取到不同的狀態對象中,允許狀態對象在其內部狀態發生改變時,改變其行為
比如:
通過按鈕控制一個電梯的狀態,有開門狀態,關門狀態,停止狀態,運行狀態。每一種狀態改變,都有可能要根據其他狀態來更新處理
比如如果電梯處于運行狀態,就不能進行開們操作,而電梯是停止狀態,就可以執行開門操作
題目描述:
- 一個對象的行為決定于它的狀態,并且它必須在運行時刻根據狀態改變它的行為
- 一個對象在其內部狀態改變時改變其行為
- 一個對象的行為決定于它的狀態,并且它必須在運行時刻根據狀態改變它的行為
- 一個操作中含有龐大的多分支的條件語句,且這些分支依賴于該對象的狀態
4.9 策略模式
意圖:
定義一系列的算法,把他們一個個封裝起來,并且使他們可以相互替換。此模式使得算法可以獨立于使用他們的客戶而變化
比如:
我們要去旅游選擇出行方式,可以選擇騎自行車,坐汽車,坐火車,坐飛機
優點:
- 策略模式之間可以自由切換,由于策略類都實現同一個接口,所以使他們之間可以自由切換
- 易于擴展,增加一個新的策略只需要添加一個具體的策略類,基本不需要改變原有的代碼,符合“開閉原則”
- 避免使用多重條件選擇語句,充分體現面向對象設計思想
缺點:
- 客戶端必須知道所有的策略類,并且自行決定使用哪一個策略類
- 策略模式將造成產生很多策略類
題目描述:
- 需要使用一個算法的不同變體
- 許多相關的類僅僅是行為有異
- 在設計某購物中心的收銀軟件系統時,要求能夠支持在不同時期推出打折,返利,滿減等不同促銷活動
- 一個類定義了多種行為,并且這些行為在這個類的操作以多個條件語句的形式出現
4.10 訪問者模式
意圖:
封裝一些作用于某對象結構中的各元素的操作。它允許在不改變各元素的類的前提下定義作用于這些元素的新操作
比如:
有很多寵物,狗、貓等,要給寵物喂食的話,主人可以喂食,其他人也可以喂食
題目描述:
- 一個對象結構包含很多類對象,它們有不同的接口,而用戶想對這些對象實施一些依賴于其具體類的操作
- 需要對一個對象結構中的對象進行很多不同的并且不相關的操作,而又想避免這些操作“污染”這些對象的類
- 定義對象結構的類很少改變,但經常需要在此結構上定義新的操作
4.11 模版方法模式(下午題不考)
意圖:
定義一個操作中的算法骨架,而將一些步驟延遲到子類中,模版方法模式使得子類可以不改變一個算法結構即可重定義該算法的某些特定步驟
題目描述:
- 一次性實現一個算法的不變部分,并將可變的行為留給子類來實現
- 各子類中公共的行為應被提取出來并集中到一個公共父類中,以避免代碼重復
- 控制子類擴展
4.12 行為性設計模式總結
行為型設計模式 | 定義 | 關鍵字 |
---|---|---|
職責鏈模式(Chain of Responsibility)(責任鏈模式) | 使多個對象都有機會處理請求,從而避免請求的發送者和接受者之間的耦合關系。將這些對象連成一條鏈,并沿著這條鏈傳遞該請求,直到有一個對象處理它為止 | 傳遞請求,職責,鏈接 |
命令模式(Command) | 將一個請求封裝為一個對象,從而使得可以用不同的請求對客戶進行參數化,對請求排隊或記錄請求日志,以及支持可撤銷的操作 | 參數化,日志記錄 |
解釋器模式(Interpreter) | 給定一個語言,定義他的文法的一種表示,并定義一個解釋器,這個解釋器使用該表示來解釋語言中的句子 | 文法,解釋 |
迭代器模式(Iterator) | 提供一種方法順序訪問一個聚合對象中的各個元素,且不需要暴露該對象的內部表示 | 順序訪問 |
中介者模式(Mediator) | 用一個中介對象來封裝一系列的對象交互,中介者使各個對象不需要顯式的互相引用,從而使其耦合松散,而且可以獨立地改變他們之間的交互 | 不直接引用 |
備忘錄模式(Memento) | 在不破壞封裝性的前提下捕獲一個對象的內部狀態,并在對象之外保存這個狀態。這樣以后就可以將對象恢復到原先保存的狀態 | 保存,恢復 |
觀察者模式(Observer) | 定義對象間的一種一對多的依賴關系,當一個對象的狀態發生改變時,所有依賴于它的對象都得到通知并被自動更新 | 通知,自動更新 |
狀態模式(State) | 允許一個對象在其內部狀態改變時改變它的行為。對象看起來似乎修改了它的類 | 狀態變成類 |
策略模式(Strategy) | 定義一系列的算法,把他們一個個封裝起來,并且使他們可以相互替換。此模式使得算法可以獨立于使用他們的客戶而變化 | 算法替換 |
模版方法模式(Template Method) | 定義一個操作中的算法骨架,而將一些步驟延遲到子類中,模版方法模式使得子類可以不改變一個算法結構即可重定義該算法的某些特定步驟 | |
訪問者模式(Visitor) | 封裝一些作用于某對象結構中的各元素的操作。它允許在不改變各元素的類的前提下定義作用于這些元素的新操作 | 新操作 |