設計模式:Template Method(模板方法)
阿新 • • 發佈:2021-02-16
意圖
定義一個操作中的演算法的骨架,而將一些步驟延遲到子類中。Template Method 使得子類可以不改變一個演算法的結構即可重定義該演算法的某些特定步驟。(個人理解:任務流程部分步驟延遲實現,如一些框架的操作已經擬定了主流程,我們只需要實現部分虛擬函式介面)
適用性
模板方法應用於下列情況:
- 一次性實現一個演算法的不變的部分,並將可變的行為留給子類來實現。(個人理解,對於哪些是穩定需求的判定本身也需要行業經驗,所以一開始設計沒必要過於套用模式,而是在逐步迭代中完善)
- 各子類中公共的行為應被提取出來並集中到一個公共父類中以避免程式碼重複。
- 控制子類擴充套件,只在特定的點進行擴充套件。(設計模式一書中把預設實現的虛擬函式留作為擴充套件介面,個人認為一些框架中的xxBefore 和 xxAfter 介面應屬此列)
結構
AbstractClass
- 定義抽象的原語操作(Primitive Operation),具體的子類將重定義它們以實現一個演算法的各步驟。
- 實現一個模板方法,定義一個演算法的骨架。該模板方法不僅呼叫原語操作,也呼叫定義在類中的其他操作。
ConcreteClass
- 實現原語操作以完成演算法中與特定子類相關的步驟。
ConcreateClass 靠 AbstreactClass 來實現演算法中不變的步驟。
細則
- 在 GOF 《設計模式》一書中區分了鉤子函式和抽象函式,鉤子函式預設有預設行為作為保留的擴充套件介面,抽象函式表示必須重定義的介面。
- 使用C++訪問控制。在C++中,一個模板方法呼叫的原語操作可以被定義為保護成員。這保證它們只被模板方法呼叫。必須重定義的原語操作必須定義為純虛擬函式。模板方法自身不需被重定義;因此可以將模板方法定義為一個非虛成員函式。
- 儘量減少原語操作。定義模板方法的一個重要目的就是儘量將少一個子類具體實現該演算法時必須重定義的那些原語運算元目。需要重定義的操作越多,客戶程式越冗長。
- 命名約定。可以給應被重定義的操作的名字加上一個字首以識別它們,如 DoXxxx() 。
程式碼示例
class Library { public: void run() { step1(); step2(); if (step3()) { step4(); } } protected: void step1() {} void step2() {} //鉤子函式有預設實現 virtual bool step3() { return true; } //抽象介面 virtual void step4() = 0; }; class MyLibrary : public Library { protected: //實現具體操作 void step4() override {} }; int main() { MyLibrary lib; lib.run(); return 0; }
相關模式
- Factory Method 常被 Template Method 呼叫。
- Template Method 使用繼承來改變演算法的一部分,Strategy 使用委託來改變整個演算法。(個人理解:GOF 是以 C++ 語言來描述的,但是模式本身可以超越語言的限制,對於 C 語言就沒有類繼承這種機制。我的看法是,模板方法在於封裝流程,將可變部分的實現延遲到子類或者初始化,即便是用函式指標而不是類繼承我也認為是模板方法的思想;而策略模式的重點在於以擴充套件的思維應對需求變更,增加實體類比增加 if else/switch case 更符合開閉原則。)
參照
書籍:GOF《設計模式:可複用面向物件軟體的基礎》
視訊:李建忠設計模式(版權問題,自行搜尋)