欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

常见设计模式之(四):模板模式

程序员文章站 2022-05-26 07:50:01
...

常见设计模式之(四):模板模式

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;
}

编译并运行

aaa@qq.com:/wan/04templet# g++ TemplateClient.cpp -std=c++11  
aaa@qq.com:/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;
}

编译并运行

aaa@qq.com:/wan/04templet# g++ TemplateClientEx.cpp -std=c++11
aaa@qq.com:/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 模板方法的缺点

暂时没有相关缺点。