iT邦幫忙

2023 iThome 鐵人賽

DAY 13
0
Software Development

深入淺出設計模式 - 使用 C++系列 第 13

[Day 13] 封裝演算法元素 - 樣板方法模式 (Template Method Pattern)

  • 分享至 

  • xImage
  •  

定義

  • Template Method is a behavioral pattern that allows you to defines a skeleton of an algorithm in a base class and let subclasses override the steps without changing the overall algorithm’s structure
  • UML 示意
    https://ithelp.ithome.com.tw/upload/images/20230925/20138643mVBB2nUrmb.jpg

組成

  • Abstract Class: 直接實例化的 Class。存在的目的主要是為了定義一個公共的 Interface 和演算法框架,供其子類別繼承和實現
    • Algorithm Skeleton: 確定算法的基本結構和執行流程
    • Abstract Methods: 由子類實現的方法,通常沒有預設實作
    • 程式碼片段:
    class AbstractClass {
    public:
        virtual void step1() = 0;
        virtual void step2() = 0;
        virtual void step3() = 0;
    };
    
  • Concrete Class: 具體類是繼承自抽象類的類,它必須實現抽象類中所有的抽象方法。具體類的實例可以直接創建和使用
    • 程式碼片段:
    class ConcreteClass1 : public AbstractClass {
    public:
        void step1() override {
            // 具體實現
        }
        void step2() override {
            // 具體實現
        }
        void step3() override {
            // 具體實現
        }
    };
    
    class ConcreteClass2 : public AbstractClass {
    public:
        void step1() override {
            // 具體實現2
        }
        void step2() override {
            // 具體實現2
        }
        void step3() override {
            // 具體實現2
        }
    };
    
  • Hook Method (Optional): 是一種特殊的方法,通常在抽象類中有預設實現,但可以被具體類覆寫(Override)。它通常用於擴展或修改算法骨架中的某些步驟,而不需要改變整個算法的結構
    • 在抽象類中宣告
    class AbstractClass {
    public:
        virtual void hook() {
            // 預設實現(可為空)
        }
    };
    
    • 在具體類中宣告
    class ConcreteClass : public AbstractClass {
    public:
        void hook() override {
            // 客製化實現
        }
    };
    

範例 (翻譯自 Guru)

/**
 * 抽象類定義了一個模板方法,該方法包含某種算法的骨架,
 * 由對(通常是)抽象基本操作的呼叫組成。
 * 
 * 具體子類應實現這些操作,但保持模板方法本身不變。
 */
class AbstractClass {
  /**
   * 模板方法定義算法的骨架。
   */
 public:
  void TemplateMethod() const {
    this->BaseOperation1();
    this->RequiredOperations1();
    this->BaseOperation2();
    this->Hook1();
    this->RequiredOperation2();
    this->BaseOperation3();
    this->Hook2();
  }
  /**
   * 這些操作已經有實現。
   */
 protected:
  void BaseOperation1() const {
    std::cout << "AbstractClass:我正在做大部分的工作\n";
  }
  void BaseOperation2() const {
    std::cout << "AbstractClass:但我允許子類覆寫一些操作\n";
  }
  void BaseOperation3() const {
    std::cout << "AbstractClass:但我仍然在做大部分的工作\n";
  }
  /**
   * 這些操作必須在子類中實現
   */
  virtual void RequiredOperations1() const = 0;
  virtual void RequiredOperation2() const = 0;
  /**
   * 這些是"Hook"。子類可能覆寫它們,但這不是必須的,
   * 因為Hook已經有預設(但空的)實現。鉤子在算法的一些關鍵位置提供額外的擴展點。
   */
  virtual void Hook1() const {}
  virtual void Hook2() const {}
};

/**
 * 具體類必須實現基類的所有抽象操作
 * 它們也可以覆寫具有預設實現的一些操作
 */
class ConcreteClass1 : public AbstractClass {
 protected:
  void RequiredOperations1() const override {
    std::cout << "ConcreteClass1:實現Operation1\n";
  }
  void RequiredOperation2() const override {
    std::cout << "ConcreteClass1:實現Operation2\n";
  }
};

/**
 * 通常,具體類只覆寫基類操作的一部分
 */
class ConcreteClass2 : public AbstractClass {
 protected:
  void RequiredOperations1() const override {
    std::cout << "ConcreteClass2:實現Operation1\n";
  }
  void RequiredOperation2() const override {
    std::cout << "ConcreteClass2:實現Operation2\n";
  }
  void Hook1() const override {
    std::cout << "ConcreteClass2:覆寫Hook1\n";
  }
};

/**
 * 客戶端代碼調用模板方法以執行算法。客戶端代碼不必知道它所使用的對象的具體類
 * 只要它通過基類的接口與對象交互即可
 */
void ClientCode(AbstractClass *class_) {
  // ...
  class_->TemplateMethod();
  // ...
}

int main() {
  std::cout << "同一客戶端代碼可以與不同的子類一起工作:\n";
  ConcreteClass1 *concreteClass1 = new ConcreteClass1;
  ClientCode(concreteClass1);

  std::cout << "\n";
  
  std::cout << "同一客戶端代碼可以與不同的子類一起工作:\n";
  ConcreteClass2 *concreteClass2 = new ConcreteClass2;
  ClientCode(concreteClass2);
}

Output:

// ConcreteClass1
同一客戶端代碼可以與不同的子類一起工作:
AbstractClass:我正在做大部分的工作
ConcreteClass1:實現Operation1
AbstractClass:但我允許子類覆寫一些操作
ConcreteClass1:實現Operation2
AbstractClass:但我仍然在做大部分的工作

// ConcreteClass2
同一客戶端代碼可以與不同的子類一起工作:
AbstractClass:我正在做大部分的工作
ConcreteClass2:實現Operation1
AbstractClass:但我允許子類覆寫一些操作
ConcreteClass2:覆寫Hook1
ConcreteClass2:實現Operation2
AbstractClass:但我仍然在做大部分的工作

Reference

  1. https://refactoring.guru/design-patterns/template-method/cpp/example
  2. https://ithelp.ithome.com.tw/articles/10220163
  3. https://www.tutorialspoint.com/design_pattern/template_pattern.htm

上一篇
[Day 12] 回顧比較 — Decorator / Adapter / Facade 三種 Design Patterns
下一篇
[Day 14] 把迭代封裝起來 — 迭代模式 (Iterator Design Pattern)
系列文
深入淺出設計模式 - 使用 C++37
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言