常見設計模式之(四):模板模式

常見設計模式之(四):模板模式

1 什麼是模板模式(template pattern)?

摘自 java 設計模式官網 中 有關模板模式的定義:

Define the skeleton of an algorithm in an operation, deferring some steps to subclasses. Template method lets subclasses redefine certain steps of an algorithm without changing the algorithm’s structure.

翻譯一下:
模板模式定義一個算法的架構,將一些步驟推遲到子類中執行,模板模式讓在不改變算法結構的情況下重新定義該算法的某些特定步驟。
模板模式中的方法有兩類:

  • 基本方法:基本方法也就是基本操作,基類只是定義抽象的接口,並且被模板方法調用。
  • 模板方法:一般是一個或者幾個方法,通常爲一個算法,也就是算法或者程序的框架,實現對基本方法的調度,完成固定的邏輯;這一部分一般不能被子類覆蓋。

2 模板模式基於c++的實現

模板模式的通用類圖如下:
在這裏插入圖片描述

具體的代碼如下

AbstractTempletClass.h

#ifndef ABSTRACT_CLASS_H
#define ABSTRACT_CLASS_H
class AbstractTempletClass
{
public:
    AbstractTempletClass()
    {

    }
    ~AbstractTempletClass()
    {
        
    }
    virtual void do_somethingA() = 0;
    virtual void do_somethingB() = 0;
    virtual void do_somethingC() = 0;
    virtual void FrameMethod() final   // 利用c++11中的final關鍵字 禁止子類覆蓋基類中方法
    {
        do_somethingA();
        do_somethingB();
        do_somethingC();
    }
};
#endif

ConcreteClass1.h

#ifndef CONCRETE_CLASS_ONE_H
#define CONCRETE_CLASS_ONE_H

#include <stdio.h>
#include "AbstractClass.h"

class ConcreteClass1 : public AbstractTempletClass
{
public:
      ConcreteClass1()
      {

      }
      ~ConcreteClass1()
      {

      }
      virtual void do_somethingA() override  // override c++11 子類必須覆蓋父類的方法,
      {
          printf("%s %s %d \n",__FILE__,__FUNCTION__,__LINE__);
      }
      virtual void do_somethingB() override
      {
          printf("%s %s %d \n",__FILE__,__FUNCTION__,__LINE__);
      }
      virtual void do_somethingC() override
      {
          printf("%s %s %d \n",__FILE__,__FUNCTION__,__LINE__);
      } 
};

#endif

ConcreteClass2.h

#ifndef CONCRETE_CLASS_TWO_H
#define CONCRETE_CLASS_TWO_H

#include <stdio.h>
#include "AbstractClass.h"

class ConcreteClass2 : public AbstractTempletClass
{
public:
      ConcreteClass2()
      {

      }
      ~ConcreteClass2()
      {

      }
      virtual void do_somethingA() override 
      {
          printf("%s %s %d \n",__FILE__,__FUNCTION__,__LINE__);
      }
      virtual void do_somethingB() override
      {
          printf("%s %s %d \n",__FILE__,__FUNCTION__,__LINE__);
      }
      virtual void do_somethingC() override
      {
          printf("%s %s %d \n",__FILE__,__FUNCTION__,__LINE__);
      } 
};
#endif

TemplateClient.cpp

#include "AbstractClass.h"
#include "ConcreteClass1.h"
#include "ConcreteClass2.h"

int main(void)
{
    AbstractTempletClass  * concrete1 = new ConcreteClass1();
    AbstractTempletClass  * concrete2 = new ConcreteClass2();
    concrete1->FrameMethod();
    concrete2->FrameMethod();
    delete concrete2;
    delete concrete1;
    return 0;
}

編譯並運行

root@wan:/wan/04templet# g++ TemplateClient.cpp -std=c++11  
root@wan:/wan/04templet# ./a.out 
ConcreteClass1.h do_somethingA 20 
ConcreteClass1.h do_somethingB 24 
ConcreteClass1.h do_somethingC 28 
ConcreteClass2.h do_somethingA 20 
ConcreteClass2.h do_somethingB 24 
ConcreteClass2.h do_somethingC 28 

通過上述方法實現了服用調用子類的方法,具體的接口由子類進行實現,而父類的框架保持不變。

3 模板模式的擴展 (帶鉤子方法的模板模式)

類圖
在這裏插入圖片描述
代碼實現如下

AbstractTempletClassEx.h

#ifndef ABSTRACT_CLASS_EX_H
#define ABSTRACT_CLASS_EX_H

class AbstractTempletClassEx
{
public:
    AbstractTempletClassEx()
    {

    }
    ~AbstractTempletClassEx()
    {
        
    }
    virtual void do_somethingA() = 0;
    virtual void do_somethingB() = 0;
    virtual void do_somethingC() = 0;
    virtual int execution_sequence() = 0;
    virtual void FrameMethod() final
    {
        if(execution_sequence() == 1)
        {
            do_somethingA();
            do_somethingB();
            do_somethingC();
        }
        else if(execution_sequence() == 2)
        {
            do_somethingC();
            do_somethingB();
            do_somethingA();
        }
        else
        {
            /* code */
        }
        
        
    }
};
#endif

ConcreteClass1Ex.h

#ifndef CONCRETE_CLASS_ONE_EX_H
#define CONCRETE_CLASS_ONE_EX_H
#include <stdio.h>
#include "AbstractClassEx.h"
class ConcreteClass1Ex : public AbstractTempletClassEx
{
public:
      ConcreteClass1Ex()
      {
      }
      ~ConcreteClass1Ex()
      {
      }
      virtual void do_somethingA() override
      {
          printf("%s %s %d \n",__FILE__,__FUNCTION__,__LINE__);
      }
      virtual void do_somethingB() override
      {
          printf("%s %s %d \n",__FILE__,__FUNCTION__,__LINE__);
      }
      virtual void do_somethingC() override
      {
          printf("%s %s %d \n",__FILE__,__FUNCTION__,__LINE__);
      }
      virtual int execution_sequence() override
      {
          return 1;
      } 
};
#endif

ConcreteClass2Ex.h

#ifndef CONCRETE_CLASS_TWO_EX_H
#define CONCRETE_CLASS_TWO_EX_H

#include <stdio.h>
#include "AbstractClassEx.h"

class ConcreteClass2Ex : public AbstractTempletClassEx
{
public:
      ConcreteClass2Ex()
      {

      }
      ~ConcreteClass2Ex()
      {

      }
      virtual void do_somethingA() override 
      {
          printf("%s %s %d \n",__FILE__,__FUNCTION__,__LINE__);
      }
      virtual void do_somethingB() override
      {
          printf("%s %s %d \n",__FILE__,__FUNCTION__,__LINE__);
      }
      virtual void do_somethingC() override
      {
          printf("%s %s %d \n",__FILE__,__FUNCTION__,__LINE__);
      } 
      virtual int execution_sequence() override
      {
          return 2;
      } 
};
#endif

TemplateClientEx.cpp

#include "AbstractClassEx.h"
#include "ConcreteClass1Ex.h"
#include "ConcreteClass2Ex.h"

int main(void)
{
    AbstractTempletClassEx  * concrete1Ex = new ConcreteClass1Ex();
    AbstractTempletClassEx  * concrete2Ex = new ConcreteClass2Ex();
    concrete1Ex->FrameMethod();
    concrete2Ex->FrameMethod();
    delete concrete2Ex;
    delete concrete1Ex;
    return 0;
}

編譯並運行

root@wan:/wan/04templet# g++ TemplateClientEx.cpp -std=c++11
root@wan:/wan/04templet# ./a.out 
ConcreteClass1Ex.h do_somethingA 20 
ConcreteClass1Ex.h do_somethingB 24 
ConcreteClass1Ex.h do_somethingC 28 
ConcreteClass2Ex.h do_somethingC 28 
ConcreteClass2Ex.h do_somethingB 24 
ConcreteClass2Ex.h do_somethingA 20 

我們利用抽象類中的execution_sequence() 方法影響到了模板方法FrameMethod()的執行結果,該方法通常稱爲鉤子方法(hook method),利用鉤子方法讓模板方法得以完美的運行,由子類方法的返回值影響公共模板方法執行結果。

4 模板方法的應用場景以及優缺點

4.1 模板方法的應用場景

  • 當多個子類有公有的方法,並且基本邏輯相同時
  • 重要、複雜的算法,可以把核心算法設計成模板方法,周圍的相關細節功能則由各個子類進行實現。
  • 當進行代碼重構時,模板模式是一個經常使用的模式,把相同的代碼抽取到父類中,然後通過鉤子函數約束父類的某些行爲。

4.2 模板方法的優點

  • 封裝不變代碼(基類模板方法)、擴展可變部分(子類實現基本方法)
  • 提煉公共的代碼,便於代碼的維護,如果項目中代碼有幾乎相同的部分,那麼應該考慮代碼的精煉或者重構。維護多處相同代碼增加軟件工程師的工作量。
  • 父類控制行爲,子類負責實現 :基本方法由子類進行實現,子類就可以通過擴展的方式增加功能,符合開閉原則。

4.3 模板方法的缺點

暫時沒有相關缺點。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章