[設計模式]二十三種設計模式
"組件協作"模式
組件協作: 實現框架與應用程序的劃分, 通過晚綁定來實現框架與應用程序之間的松耦合, 是二者之間協作時常用的模式
- 晚綁定: 從設計者去綁定用戶的代碼, 而不是用戶去綁定設計者的代碼
模板方法模式(Template Method)
模板方法模式: 定義算法的骨架(穩定), 將一些步驟延遲(變化)到子類中, 使子類可以不改變(複用)算法的結構即可重定義(override 重寫)該算法的某些特定步驟。
- 設計者設計好整個流程, 穩定的部分直接由設計者負責, 而變化的部分由用戶負責
實現: 設計者把穩定的部分實現(比如某一個步驟、run函數來負責整個運行的流程), 而把變化的部分設計為虛函數(通常是protected), 而用戶繼承設計者的類, 再實現虛函數, 最後調用設計者預設的運行函數(run函數等)
使用時機: 算法的流程穩定, 但是具體步驟的實現細節變化, 那麼就適合使用模板方法模式, 比如oat++, 它提供了HTTP服務器的流程, 但具體的實現細節從用戶自行實現
特點:
- 晚綁定: 從設計者去綁定用戶的代碼, 而不是用戶去綁定設計者的代碼
- 靈活性低: 程序的流程由設計者決定, 而用戶只能調用設計者提供的開始接口
- 易編寫: 客戶只需要實現虛函數, 不需要自己編寫執行流程。使用"不要調用我, 讓我調用你"的反向控制結構
策略模式(Strategy)
策略模式: 定義一系統算法, 把它們一個個封裝起來, 并且使它們可互相替換(變化)。該模式使得算法可獨立於使用它的客戶程序(穩定)而變化(擴展, 子類化)
- 設計者確定好策略(契約), 使用子類實現具體策略類, 用戶根據契約的內容, 使用設計者實現的行為
實現: 使用接口定義好策略(契約), 通過子類實現契約的功能, 用戶通過多態去使用對應的具體策略類
使用時機:
- 算法的流程穩定, 但流程中的某個獨立步驟存在多種不同的實現方式(變化)
- 需要使用條件判斷語句的地方, 可以通過多態來直接區分情況, 但如果是穩定的條件判斷, 那麼就不需要使用策略模式
特點:
- 易擴展: 需要新的實現細節, 可以直接使用子類去實現, 不需要修改接口的方法
- 客戶職責增加: 用戶必須需要全部的策略, 才能正確地使用對應的策略實例
觀察者模式(Observer/Event)
觀察者模式: 定義對象間一對多(變化)的依賴關係, 以便當一個對象的狀態發生改變時, 所有依賴於它的對象都能得到通知并自動更新
- 目標發送通知時, 無需指定觀察者, 通知(可以携帶通知信息作為參數)會自動傳播
- 觀察者自己決定是否需要訂閱通知, 目標對象對此一無所知
實現: 對象存儲依賴它的對象的信息, 當狀態改變時, 通知依賴它的對象, 也就是調用依賴它的對象的方法
使用時機: 一個對象的改變需要觸發其他多個對象的動作
"單一職責"模式
"單一職責"模式: 劃清責任, 在軟件組件的設計中, 如果責任劃分的不清晰, 使用繼承得到的結果往往是隨著需求的變化, 子類急劇膨脹, 同時充斥著重復代碼
裝飾模式(Decorator)
裝飾模式: 動態(組合)地給一個對象增加一些額外的職責。就增加功能而言, Decorator模式比生成子類(繼承)更為靈活(消除重復代碼&減少子類個數)
- 裝飾: 為組件(接口)提供新的功能(職責), 相當於裝飾組件(加上新的東西上去)
實現: 主要看裝飾者和具體裝飾者, 實際上可以沒有裝飾者直接使用具體裝飾者也能接受
- 組件: 接口
- 具體組件: 實現接口的原始對象類, 被裝飾的對象
- 裝飾者: 抽象類(因為需要組合組件), 通常繼承組件同時組合組件
- 繼承: 讓裝飾者偽裝成一個組件, 那麼一個裝飾者也可以被另一個裝飾者繼續包裹, 那麼就可以疊加不同的職責, 而不用新寫一個疊加對應職責的具體裝飾者
- 組合: 裝飾者只實現新增的功能, 本來組件的功能由具體組件實現
- 具體裝飾者: 為組件添加新的職責, 它的方法通常會實現新的職責再調用組合組件的方法
使用時機: 透明地為一個對象增加職責, 特別是需要新增多個職責的情況下
橋模式(Bridge)
橋模式: 將抽象部分(業務功能)與實現部分(平台實現)分離, 使它們都可以獨立地變化
- 橋: 連接兩個類, 抽象功能組合實現部分
實現:
- 實現部分: 接口
- 抽象部分: 抽象類, 其中有著實現部分的接口的指針
使用場景: 當一個類有著多個不同維度的方向, 那麼就需要把每個維度的方向獨立出來
"對象創建"模式
"對象創建"模式: 解開new的緊耦合, 來避免對象創建(new)過程中所導致的緊耦合,(依賴具體類) 從而支持對象創建的穩定
工廠模式
工廠模式: 定義一個用於創建對象的接口, 讓子類決定實例化哪一個類, Factory Method使得一個類的實例化延遲(目的: 解耦, 手段: 虛函數)到子類
- 把new部分的依賴, 從子類中調離, 而進行參數的方式指定
實現:
-
組件: 接口
-
具體組件: new部分使用工廠組件的函數, 其中工廠組件實例從參數傳入
-
工廠組件: 接口
-
具體工廠組件: 返回對應的具體組件
-
產品 (Product):定義了工-廠方法所創建對象的接口。
-
具體產品 (Concrete Product):實現 Product 接口。
-
創建者 (Creator):一個抽象類,聲明了返回 Product 對象的工廠方法(通常是純虛函數)。它通常還包含依賴於該工廠方法的、使用產品的業務邏輯。
-
具體創建者 (Concrete Creator):重寫工廠方法,以返回一個具體產品的實例。
使用時機: 創建對象的程式碼與使用對象的程式碼分離開時。
抽象工廠模式
抽象工廠模式: 提供一個接口, 讓該接口負責創建一系列"相關或者相互依賴的對象", 無需指定它們具體的類
- 和工廠模式的分別是幾個對象之間有關係, 也就是幾個對象應該以特定的方式組合在一起
實現:
- 抽象工廠 (Abstract Factory):
- 這是一個接口。
- 它聲明了一組用於創建抽象產品的方法。每個方法對應一個產品。
- 具體工廠 (Concrete Factory):
- 實現了 Abstract Factory 接口。
- 它負責創建一整套具體的產品。工廠內部的每個創建方法都會返回一個具體的產品實例。
- 抽象產品 (Abstract Product):
- 這也是一個接口。
- 它為某一種類型的產品(例如,按鈕、CPU)定義了公共接口。
- 具體產品 (Concrete Product):
- 實現了 Abstract Product 接口。
- 它們是由具體工廠創建的對象。同一個具體工廠創建的所有產品,構成了一個「產品族」。
- 客戶端 (Client):
- 只使用 Abstract Factory 和 Abstract Product 的接口。
- 它與任何具體的工廠或產品類完全解耦。
使用時機: 需要生成一系統有關係的工廠方法
原型模式
原型模式: 使用原型實例指定創建對象的種類, 然後通過拷貝這些原型來創建新的對象
實現:
- 原型: 抽口, 有著clone方法
- 具體原型: 實現clone方法
使用時機: 需要創建多個構建流程複雜的類, 在創建一個之後把它作為原型, 不斷生成對象
- 比如創建進程fork
構建器模式
構建器模式: 將一個複雜對象的構建與其表示相分離, 使得相同的構建過程(穩定)可以創建不同的表示(變化)
實現:
- 構建器: 接口, 聲明構建步驟
- 具體構建器
- 組件: 複雜對象, 有著很多的構建方法
- 指導者: 封裝構建流程, 但實際上很少使用, 因為這樣就固定了整個流程, 需要組件間有著類似的流程才可以使用, 導致復用性很低
使用時機: 分離對象的構建和使用
對象性能模式
"對象性能"模式: 針對創建很多對象的性能問題
單件模式
單件模式: 保證一個類僅有一個實例, 并提供一個該實例的全局訪問點
實現:
- static: C++11及以後版本, 確保了函數內的靜態局部變量的初始化是線程安全的, 所以可以直接使用static修飾變量, 最後返回靜態局部變量
- 雙重檢查鎖模式: 為了線程安全和性能要求, 所以需要有原語來確保線程安全和兩次判斷條件確保性能要求
// 這是 DCLP 的一種實現,試圖通過精細的內存序來優化性能
Singleton* Singleton::getInstance() {Singleton* tmp = m_instance.load(std::memory_order_relaxed);std::atomic_thread_fence(std::memory_order_acquire); // 獲取 fenceif (tmp == nullptr) { // 第一次檢查(在鎖外)std::lock_guard<std::mutex> lock(m_mutex);tmp = m_instance.load(std::memory_order_relaxed);if (tmp == nullptr) { // 第二次檢查(在鎖內)tmp = new Singleton;std::atomic_thread_fence(std::memory_order_release); // 釋放 fencem_instance.store(tmp, std::memory_order_relaxed);}}return tmp;
}
使用場景: 管理全局唯一的資源, 比如日志、配置文件等
- 但使用依賴注入是更好的方式
享元模式
享元模式: 運用共享技術有效地支持大量細粒度的對象
實現: 對象池記錄全部的元件, 在使用的時候直接在對象池中尋找, 不存在的話加入進對象池
使用場景: 大量的對象, 如果可以區分出能共享和獨立的數據, 那麼就可以把共享的數據記錄起來
接口隔離模式
"接口隔離"模式: 在組件構建過程中,某些接口之間直接的依賴常常會帶來很多問題、甚至根本無法實現。採用添加一層間接(穩定)接口,來隔離本來互相緊密關聯的接口是一種常見的解決方案。
門面模式
門面模式: 為子系統中的一組接口提供一個一致(穩定)的界面, 門面模式定義了一個高層接口, 這個接口使得這一子系統更加容易使用(複用)
- 解耦: 隔離組件和組件的關係, 組件只需要知道另一組件的門面, 而不需要知道內部細節
實現:
- 門面類: 組合需要用的部分, 實現簡化調用流程
使用場景:
- 當你想為一個複雜的庫或框架提供一個簡單的接口時(例如,封裝一個複雜的第三方庫)。
- 當你想將系統劃分為多個層次時,可以使用門面來定義每個層次與其他層次交互的唯一入口。
- 當一個遺留系統過於複雜,你想為其提供一個更清晰的接口時。
代理模式
代理模式: 為其他對象提供一種代理, 用於控制(隔離, 使用接口)對這個對象的訪問
- 附加行為: 不改變原始對象核心行為的前提下,為其提供訪問控制或管理的附加行為。
實現:
- 主題: 接口
- 具體主題: 具體實現
- 代理: 轉發行為, 轉發之前先實現想要的功能
使用場景: 給類的行為附加上額外的信息
適配器模式
適配器模式: 將一個類的接口轉換成客戶希望的另一個接口, 使得原本由於接口不兼容而不能一起工作的類可以一起工作
實現:
- 目標 (Target):
- 這是一個接口或抽象類。
- 它定義了客戶端期望的接口。
- 被適配者 (Adaptee):
- 這是一個已有的類。
- 它的接口與 Target 接口不兼容。
- 適配器 (Adapter):
- 實現 (implements) Target 接口。
- 內部持有 (has-a) 一個 Adaptee 對象的引用(這就是組合)。
- 當客戶端調用 Adapter 的方法時,Adapter 內部會將這個調用轉換成對 Adaptee 對象一個或多個方法的調用。
- 客戶端 (Client):
- 只與 Target 接口進行交互。
使用場景: 為了複用已有組件的行為, 不需要額外再次實現
中介者模式
中介者模式: 用一個中介對象來封裝(封裝變化)一系統的對象交互。中介者使各對象不需要顯式的相互引用(編譯時依賴->運行時依賴), 從而使其耦合松散(管理變化), 而且可以獨立地改變它們之間的交互
實現:
- 中介者 (Mediator):
- 這是一個接口。
- 它定義了與各個同事 (Colleague) 對象通信的方法。
- 具體中介者 (Concrete Mediator):
- 實現了
Mediator接口。 - 它知道並維護所有的
Colleague對象。 - 它負責實現協調邏輯,接收來自一個
Colleague的消息,並決定將消息轉發給哪些其他的Colleagues。
- 實現了
- 同事 (Colleague):
- 這通常是一個抽象類或接口。
- 它定義了所有同事類都必須遵守的接口。
- 最關鍵的是,它內部持有一個
Mediator對象的引用。
- 具體同事 (Concrete Colleague):
- 實現了
Colleague接口。 - 每個
Colleague只知道自己的業務邏輯。 - 當它需要與其他
Colleague通信時,它不會直接調用其他Colleague,而是調用它所持有的Mediator的方法,由中介者去完成後續的協調。
- 實現了
使用場景: 類和類之間的引用複雜, 使用中介者來隔離類和類的引用
"狀態變化"模式
"狀態變化"模式: 對象的狀態經常變化, 但需要保持高層模块的穩定性
狀態模式
狀態模式: 允許一個對象在其內部狀態改變時改變它的行為, 從而使對象看起來似乎修改了其行為
- 切換狀態: 不同的狀態對於同一個請求有著不同的行為, 那麼通過切換狀態就可以實現對於同一個請求實現不同的處理
實現:
- 上下文 (Context):
- 這是持有狀態的「主對象」。
- 它內部維護一個 State 接口的實例,這個實例就是它的當前狀態。
- 它將所有與狀態相關的行為委託給這個狀態對象。
- 它通常還提供一個 setter 方法,供狀態對象在需要時改變 Context 的當前狀態。
- 狀態 (State):
- 這是一個接口或抽象類。
- 它定義了所有具體狀態都必須實現的行為接口。
- 它通常還會持有一個 Context 的引用,以便能夠讀取 Context 的信息或改變 Context 的狀態。
- 具體狀態 (Concrete State):
- 實現了 State 接口。
- 每一個具體狀態類都封裝了與 Context 某一個特定狀態相關的行為。
- 它負責處理來自 Context 的請求,並在適當的時候決定下一個狀態是什麼,然後通過 Context 的 setter 方法來切換 Context 的狀態。
使用場景: 對象的行為依賴於內部狀態, 在運行時根據狀態的改變而改變行為
備忘錄模式
備忘錄模式: 在不破壞封裝性的前提下, 捕獲一個對象的內部狀態, 并在該對象之外保存這個狀態, 這樣以後就可以將該對象恢到原先保存的狀態
- 序列化代替: 現代通常不使用面向對象來實現保存狀態, 而是使用序列化實現
使用場景: 需要保存類的狀態
"數據結構"模式
"數據結構"模式: 把數據結構封裝到類中, 而不是從用戶實現
組合模式
組合模式: 將對象組合成樹形結構以表示"部分-整體"的層次結構, 使得用戶對單個對象和組合對象的使用具有一致性(穩定)
實現:
- 組件 (Component):
- 這是一個接口或抽象類。
- 它為組合中的所有對象(包括葉子和容器)定義了公共接口。
- 它通常還會聲明一些用於管理子組件的方法,如 add(), remove(), getChild()。對於這些方法的實現,有兩種流派:
- 透明模式 (Transparent):在 Component 接口中聲明所有管理子節點的方法。這樣葉子節點也擁有了這些方法,但通常是空實現或拋出異常。優點是客戶端完全透明,缺點是不夠安全。
- 安全模式 (Safe):只在 Composite 類中聲明管理子節點的方法。這樣葉子節點就不會有這些無關的方法,更安全,但客戶端需要區分葉子和容器,透明性降低。
- 葉子 (Leaf):
- 實現了 Component 接口。
- 代表了樹形結構中的終端節點。它沒有子節點。
- 組合 (Composite):
- 實現了 Component 接口。
- 代表了樹形結構中的非終端節點(容器)。
- 它的內部持有一個 Component 對象的集合(std::vector<Component*>)。
- 它實現 Component 接口的方法時,通常會遍歷其子組件,並將工作委託給它們。
- 客戶端 (Client):
- 通過 Component 接口與組合結構中的對象交互,無需區分葉子和容器。
使用場景: 對象的「部分-整體」層次結構時
迭代器模式
迭代器模式: 提供一種方法順序訪問一個聚合對象的各個元素, 而又不暴露(穩定)該對象的內部表示
- 不需要了解內部結構": 使用迭代器隔離算法和容器, 而不需要對象的實現是數組、哈希表、紅黑樹等數據結構
實現:
- 迭代器 (Iterator):
- 這是一個接口。
- 它定義了遍歷元素所需的標準操作,例如:
- next(): 返回下一個元素,並將迭代器向前移動。
- hasNext(): 檢查是否還有更多元素。
- first(): 將迭代器重置到第一個元素(可選)。
- current(): 返回當前元素(可選)。
- 具體迭代器 (Concrete Iterator):
- 實現了 Iterator 接口。
- 它負責在一個具體的聚合對象上進行遍歷,並跟蹤當前的位置(例如,數組的下標、鏈表的節點指針)。
- 它通常會持有一個對其所屬的聚合對象的引用。
- 聚合 (Aggregate):
- 這也是一個接口。
- 它聲明了一個工廠方法,用於創建一個與之對應的 Iterator 對象,例如 createIterator()。
- 具體聚合 (Concrete Aggregate):
- 實現了 Aggregate 接口。
- 它持有一個內部元素集合(例如,一個數組或鏈表)。
- 它實現了 createIterator() 方法,返回一個能夠遍歷其內部結構的 Concrete Iterator 實例。
註: C++中有了泛式編程就不需要使用到迭代器模式,
- 模板是編譯時多態, 它在編譯時就完成了多態的工作, 在運行時基本不會影響性能
- 迭代器模式使用虛函數實現, 在運行時多態, 會明顯影響性能
使用場景: 類的數據結構會改變, 使用迭代器來隔離類的算法和容器
責任鏈模式
責任鏈模式: 使多個對象都有機會處理請求, 從而避免請求的發送者和接收者之間的耦合關係, 將這些對象連成一條鏈, 并沿著這條鏈傳遞請求, 直到有一個對象處理它為止
實現
- 處理者 (Handler):
- 這通常是一個接口或抽象類。
- 它定義了一個處理請求的方法,例如 handleRequest(request)。
- 最關鍵的是,它內部持有一個指向下一個處理者 (successor) 的引用。
- 它還會實現一個設置下一個處理者的方法 setSuccessor(handler)。
- 具體處理者 (Concrete Handler):
- 實現了 Handler 接口。
- 它負責處理它感興趣的請求。
- 在 handleRequest 方法中,它會先判斷自己能否處理當前請求:
- 如果能,就處理它,流程結束。
- 如果不能,就將請求轉發給它的後繼者(successor->handleRequest(request))。
- 客戶端 (Client):
- 負責創建和組裝這條責任鏈。
- 將請求發送給鏈中的第一個處理者對象。
使用場景: 有多個對象可以處理同一個請求,而具體由哪個對象處理是在運行時動態決定的。
"數據結構"模式
"數據結構"模式: 把數據結構封裝到類中, 而不是從用戶實現
組合模式
組合模式: 將對象組合成樹形結構以表示"部分-整體"的層次結構, 使得用戶對單個對象和組合對象的使用具有一致性(穩定)
實現:
- 組件 (Component):
- 這是一個接口或抽象類。
- 它為組合中的所有對象(包括葉子和容器)定義了公共接口。
- 它通常還會聲明一些用於管理子組件的方法,如 add(), remove(), getChild()。對於這些方法的實現,有兩種流派:
- 透明模式 (Transparent):在 Component 接口中聲明所有管理子節點的方法。這樣葉子節點也擁有了這些方法,但通常是空實現或拋出異常。優點是客戶端完全透明,缺點是不夠安全。
- 安全模式 (Safe):只在 Composite 類中聲明管理子節點的方法。這樣葉子節點就不會有這些無關的方法,更安全,但客戶端需要區分葉子和容器,透明性降低。
- 葉子 (Leaf):
- 實現了 Component 接口。
- 代表了樹形結構中的終端節點。它沒有子節點。
- 組合 (Composite):
- 實現了 Component 接口。
- 代表了樹形結構中的非終端節點(容器)。
- 它的內部持有一個 Component 對象的集合(std::vector<Component*>)。
- 它實現 Component 接口的方法時,通常會遍歷其子組件,並將工作委託給它們。
- 客戶端 (Client):
- 通過 Component 接口與組合結構中的對象交互,無需區分葉子和容器。
使用場景: 對象的「部分-整體」層次結構時
迭代器模式
迭代器模式: 提供一種方法順序訪問一個聚合對象的各個元素, 而又不暴露(穩定)該對象的內部表示
- 不需要了解內部結構": 使用迭代器隔離算法和容器, 而不需要對象的實現是數組、哈希表、紅黑樹等數據結構
實現:
- 迭代器 (Iterator):
- 這是一個接口。
- 它定義了遍歷元素所需的標準操作,例如:
- next(): 返回下一個元素,並將迭代器向前移動。
- hasNext(): 檢查是否還有更多元素。
- first(): 將迭代器重置到第一個元素(可選)。
- current(): 返回當前元素(可選)。
- 具體迭代器 (Concrete Iterator):
- 實現了 Iterator 接口。
- 它負責在一個具體的聚合對象上進行遍歷,並跟蹤當前的位置(例如,數組的下標、鏈表的節點指針)。
- 它通常會持有一個對其所屬的聚合對象的引用。
- 聚合 (Aggregate):
- 這也是一個接口。
- 它聲明了一個工廠方法,用於創建一個與之對應的 Iterator 對象,例如 createIterator()。
- 具體聚合 (Concrete Aggregate):
- 實現了 Aggregate 接口。
- 它持有一個內部元素集合(例如,一個數組或鏈表)。
- 它實現了 createIterator() 方法,返回一個能夠遍歷其內部結構的 Concrete Iterator 實例。
註: C++中有了泛式編程就不需要使用到迭代器模式,
- 模板是編譯時多態, 它在編譯時就完成了多態的工作, 在運行時基本不會影響性能
- 迭代器模式使用虛函數實現, 在運行時多態, 會明顯影響性能
使用場景: 類的數據結構會改變, 使用迭代器來隔離類的算法和容器
責任鏈模式
責任鏈模式: 使多個對象都有機會處理請求, 從而避免請求的發送者和接收者之間的耦合關係, 將這些對象連成一條鏈, 并沿著這條鏈傳遞請求, 直到有一個對象處理它為止
實現
- 處理者 (Handler):
- 這通常是一個接口或抽象類。
- 它定義了一個處理請求的方法,例如 handleRequest(request)。
- 最關鍵的是,它內部持有一個指向下一個處理者 (successor) 的引用。
- 它還會實現一個設置下一個處理者的方法 setSuccessor(handler)。
- 具體處理者 (Concrete Handler):
- 實現了 Handler 接口。
- 它負責處理它感興趣的請求。
- 在 handleRequest 方法中,它會先判斷自己能否處理當前請求:
- 如果能,就處理它,流程結束。
- 如果不能,就將請求轉發給它的後繼者(successor->handleRequest(request))。
- 客戶端 (Client):
- 負責創建和組裝這條責任鏈。
- 將請求發送給鏈中的第一個處理者對象。
使用場景: 有多個對象可以處理同一個請求,而具體由哪個對象處理是在運行時動態決定的。
"行為變化"模式
"行為變化"模式: 實現組件和組件行為的解耦, 適用於在組件的構建過程中, 組件行為的變化經常導致組件本身劇列的變化
命令模式
命令模式: 將一個請求(行為)封裝一個對象, 從而使你可用不同的請求對客戶進行參數化, 對請求排列或記錄請求日志, 以及支持可撤銷的操作
實現:
- 命令 (Command):
- 這是一個接口。
- 它通常只聲明一個核心的執行方法,如 execute()。
- 有時也會包含一個 undo() 方法,用於支持撤銷操作。
- 具體命令 (Concrete Command):
- 實現了 Command 接口。
- 它的內部持有一個接收者 (Receiver) 對象的引用。
- execute() 方法的實現,就是調用接收者的一個或多個方法來完成具體的業務邏輯。
- 它還會存儲執行該方法所需的參數。
- 接收者 (Receiver):
- 真正執行業務邏輯的對象。它包含了完成請求所需的具體操作。
- Receiver 對 Command 的存在一無所知。
- 調用者 (Invoker):
- 持有一個或多個 Command 對象。
- 它不關心 Command 的具體類型,只知道在適當的時候調用 Command 的 execute() 方法。
- Invoker 就是觸發命令執行的那個角色。
- 客戶端 (Client):
- 負責創建和組裝所有對象。
- 它創建一個 Concrete Command 實例,並為其設置一個 Receiver。
- 然後,它將這個 Command 傳遞給一個 Invoker。
使用場景:
- 當你需要將請求參數化時,即將操作本身作為參數傳遞。
- 當你需要支持請求的排隊、日誌記錄或事務操作時。
- 當你需要實現撤銷 (Undo) 和重做 (Redo) 功能時
訪問器模式
訪問器模式: 一個作用於某對象結構中的各元素的操作。使得可以在不改變(穩定)各元素的類的前提下定義(擴展)作用於這些元素的新操作(變化)
實現:
- 訪問者 (Visitor):
- 這是一個接口。
- 它為對象結構中的每一個具體元素類都聲明了一個 visit 方法。例如 visit(ConcreteElementA), visit(ConcreteElementB)。
- 具體訪問者 (Concrete Visitor):
- 實現了 Visitor 接口。
- 它封裝了一個特定的演算法或操作。
- 在遍歷對象結構時,它會存儲計算的中間結果。
- 元素 (Element):
- 這是一個接口。
- 它聲明了一個核心的 accept(Visitor visitor) 方法,以一個訪問者作為參數。
- 具體元素 (Concrete Element):
- 實現了 Element 接口。
- accept 方法的實現通常只有一行:visitor.visit(this)。
- 對象結構 (Object Structure):
- 這是一個元素的集合,例如一個列表或一個組合模式的樹。
- 它通常提供一個高層次的接口,允許客戶端遍歷其所有元素(例如,通過一個迭代器),並對每個元素調用 accept 方法。
使用場景: 不會新增或刪除組件, 而且每個組件經常需要新增或刪除操作
"領域規則"模式
"領域規則"模式: 在特定領域中,某些變化雖然頻繁,但可以抽象為某種規則。這時候,結合特定領域,將問題抽象為語法規則,從而給出在該領域下的一般性解決方案。
"解析器"模式
"解析器"模式: 給定一個語言, 定義它的文法的一種表示, 并定義一種解釋器, 這個解釋器使用該表示來解釋語言中的句子
實現:
- AbstractExpression (抽象表達式):
- 定義一個解釋操作 interpret() 的介面。
- AST 中所有節點(無論是葉子節點還是非葉子節點)都需要實現這個介面。
- TerminalExpression (終端表達式):
- 實現 AbstractExpression 介面。
- 代表文法中的「終結符」,也就是最基礎的、不可再分的元素。
- 例如,在數學運算式 5 + x 中,5 和 x 就是終端表達式。
- 它的 interpret() 方法通常是直接返回自身的值或從上下文中取值。
- NonTerminalExpression (非終端表達式):
- 實現 AbstractExpression 介面。
- 代表文法中的「非終結符」,通常是規則或操作,它由一個或多個其他表達式(終端或非終端)組成。
- 例如,在 5 + x 中,+ (加法操作) 就是非終端表達式,它組合了左邊的 5 和右邊的 x。
- 它的 interpret() 方法會遞迴地呼叫其子表達式的 interpret() 方法來完成解釋。
- Context (上下文):
- 包含解釋器在解釋時需要的全域資訊或狀態。
- 例如,在解釋 a + b 時,Context 中會存儲變數 a 和 b 的具體值。
- Client (客戶端):
- 負責構建抽象語法樹,並觸發解釋操作。
- 例如,客戶端會讀取字串 “5 + x”,然後將其轉換為一個由 NumberExpression, VariableExpression, 和 AddExpression 物件組成的樹狀結構。
使用場景:
- 文法簡單:當你需要實現的語言文法非常簡單,且相對穩定,不會頻繁變更。
- 效率不是首要考量:解釋器模式通常涉及大量的類和遞迴呼叫,可能導致效能問題。如果效能是瓶頸,可能需要其他技術(如編譯器產生器)。
- 需要動態解釋和執行:當一些操作或規則需要被頻繁、動態地執行時,將它們定義為一種語言會非常靈活
