C++设计模式编程中Template Method模板方法模式的运用

准备一个抽象类,将部分逻辑以具体方法以及具体构造子的形式实现,然后声明一些抽象方法来迫使子类实现剩余的逻辑。不同的子类可以以不同的方式实现这些抽象方法,从而对剩余的逻辑有不同的实现。这就是模版方法模式的用意。

很多人可能没有想到,模版方法模式实际上是所有模式中最为常见的几个模式之一,而且很多人可能使用过模版方法模式而没有意识到自己已经使用了这个模式。模版方法模式是基于继承的代码复用的基本技术,模版方法模式的结构和用法也是面向对象设计的核心。

模版方法模式需要开发抽象类和具体子类的设计师之间的协作。一个设计师负责给出一个算法的轮廓和骨架,另一些设计师则负责给出这个算法的各个逻辑步骤。代表这些具体逻辑步骤的方法称做基本方法(primitive method);而将这些基本法方法总汇起来的方法叫做模版方法(template method),这个设计模式的名字就是从此而来。

模版方法模式中的方法

模版方法中的方法可以分为两大类:模版方法(Template Method)和基本方法(Primitive Method)。

模版方法

一个模版方法是定义在抽象类中的,把基本操作方法组合在一起形成一个总算法或一个总行为的方法。这个模版方法一般会在抽象类中定义,并由子类不加以修改地完全继承下来。

基本方法

基本方法又可以分为三种:抽象方法(Abstract Method)、具体方法(Concrete Method)和钩子方法(Hook Method)。

  • 抽象方法:一个抽象方法由抽象类声明,由具体子类实现。在C#语言里一个抽象方法以abstract关键字标示出来。
  • 具体方法:一个具体方法由抽象类声明并实现,而子类并不实现或置换。在C#语言里面,一个具体方法没有abstract关键字。
  • 钩子方法:一个钩子方法由抽象类声明并实现,而子类会加以扩展。通常抽象类给出的实现是一个空实现,作为方法的默认实现。(Visual FoxPro中项目向导建立的项目会使用一个AppHook类实现监视项目成员变化,调整系统结构的工作。)钩子方法的名字通常以do开始。

模板方法模式的实现

完整代码示例(code):模板方法模式的实现很简单,这里为了方便初学者的学习和参考,将给出完整的实现代码(所有代码采用 C++实现,并在 VC 6.0 下测试运行)。

代码片断 1:Template.h

//Template.h
#ifndef _TEMPLATE_H_
#define _TEMPLATE_H_
class AbstractClass{
  public:
  virtual ~AbstractClass();
  void TemplateMethod();
  protected:
  virtual void PrimitiveOperation1() = 0;
  virtual void PrimitiveOperation2() = 0;
  AbstractClass();
  private:
};
class ConcreteClass1:public AbstractClass{
  public:
  ConcreteClass1();
  ~ConcreteClass1();
  protected:
  void PrimitiveOperation1();
  void PrimitiveOperation2();
  private:
};
class ConcreteClass2:public AbstractClass{
  public:
   ConcreteClass2();
  ~ConcreteClass2();
  protected:
  void PrimitiveOperation1();
  void PrimitiveOperation2();
  private:
};
#endif //~_TEMPLATE_H_

代码片断 2:Template.cpp

#include "Template.h"
#include <iostream>
using namespace std;
AbstractClass::AbstractClass(){
}
AbstractClass::~AbstractClass(){
}
void AbstractClass::TemplateMethod(){
   this->PrimitiveOperation1();
  this->PrimitiveOperation2();
}
ConcreteClass1::ConcreteClass1(){
}
ConcreteClass1::~ConcreteClass1(){
}
void ConcreteClass1::PrimitiveOperation1(){
  cout<<"ConcreteClass1...PrimitiveOperat
  ion1"<<endl;
}
void ConcreteClass1::PrimitiveOperation2(){
  cout<<"ConcreteClass1...PrimitiveOperat
  ion2"<<endl;
}
ConcreteClass2::ConcreteClass2(){
}
ConcreteClass2::~ConcreteClass2(){
}
void ConcreteClass2::PrimitiveOperation1(){
cout<<"ConcreteClass2...PrimitiveOperat
ion1"<<endl;
}
void ConcreteClass2::PrimitiveOperation2(){
  cout<<"ConcreteClass2...PrimitiveOperat
  ion2"<<endl;
}

代码片断 3:main.cpp

#include "Template.h"
#include <iostream>
using namespace std;
int main(int argc,char* argv[]){
  AbstractClass* p1 = new ConcreteClass1();
  AbstractClass* p2 = new ConcreteClass2();
  p1->TemplateMethod();
  p2->TemplateMethod();
  return 0;
}

代码说明:由于模板方法模式的实现代码很简单,因此解释是多余的。其关键是将通用算法(逻辑)封装起来,而将算法细节让子类实现(多态)。

唯一注意的是我们将原语操作(细节算法)定义未保护(Protected)成员,只供模板方法调用(子类可以)。

适用场景
一次性实现一个算法的不变的部分,并将可变的行为留给子类来实现。
各子类中公共的行为应被提取出来并集中到一个公共父类中以避免代码重复。这是O p d y k e 和J o h n s o n 所描述过的“重分解以一般化”的一个很好的例子[ O J 9 3 ]。首先识别现有代码中的不同之处,并且将不同之处分离为新的操作。最后,用一个调用这些新的操作的模板方法来替换这些不同的代码。
控制子类扩展。模板方法只在特定点调用“h o o k ”操作(参见效果一节),这样就只允许在这些点进行扩展。

(0)

相关推荐

  • 深入剖析设计模式中的组合模式应用及在C++中的实现

    组合模式将对象组合成树形结构以表示"部分-整体"的层次结构.C o m p o s i t e 使得用户对单个对象和组合对象的使用具有一致性. 模式图: 适用场景: 你想表示对象的部分-整体层次结构. 你希望用户忽略组合对象与单个对象的不同,用户将统一地使用组合结构中的所有对象. 举例: namespace FactoryMethod_DesignPattern { using System; using System.Collections; abstract class Compo

  • C++设计模式编程中使用Bridge桥接模式的完全攻略

    桥接模式将抽象(Abstraction)与实现(Implementation)分离,使得二者可以独立地变化. 桥接模式典型的结构图为: 在桥接模式的结构图中可以看到,系统被分为两个相对独立的部分,左边是抽象部分,右边是实现部分,这两个部分可以互相独立地进行修改:例如上面问题中的客户需求变化,当用户需求需要从 Abstraction 派生一个具体子类时候,并不需要像上面通过继承方式实现时候需要添加子类 A1 和 A2 了.另外当上面问题中由于算法添加也只用改变右边实现(添加一个具体化子类),而右边

  • C++设计模式编程之Flyweight享元模式结构详解

    由遇到的问题引出享元模式: 在面向对象系统的设计何实现中,创建对象是最为常见的操作.这里面就有一个问题:如果一个应用程序使用了太多的对象,就会造成很大的存储开销.特别是对于大量轻量级(细粒度)的对象,比如在文档编辑器的设计过程中,我们如果为没有字母创建一个对象的话,系统可能会因为大量的对象而造成存储开销的浪费.例如一个字母"a"在文档中出现了100000 次,而实际上我们可以让这一万个字母"a"共享一个对象,当然因为在不同的位置可能字母"a"有不

  • 详解设计模式中的中介者模式在C++编程中的运用

    作用:用一个中介对象来封装一系列的对象交互.中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互. 结构图如下: Colleage抽象同事类,而ConcreteColleage是具体同时类,每个具体同事只知道自己的行为,而不了解其他同事类的情况,但它们却都认识中介者对象,Mediator是抽象中介者,定义了同事对象到中介者对象的接口,ConcreteMediator是具体中介者对象,实现抽象类的方法,它需要知道所有具体同事类,并从具体同事接受消息,向具体同事对象

  • 深入解析设计模式中的适配器模式在C++中的运用

    适配器模式属于结构型的设计模式,它是结构型设计模式之首(用的最多的结构型设计模式). 适配器设计模式也并不复杂,适配器它是主要作用是将一个类的接口转换成客户希望的另外一个接口这样使得原本由于接口不兼容而不能一起工作的那些类可以一起工作.适配器模式有两种:1.类的适配器 2.对象适配器,对象适配器更多一些. 示例:比如你在网上买了一个手机,但是买家给你发回来了一个3接头的充电器,但是恰好你又没有3接头的插槽,只有2个接口的插槽,于是你很直然地便会想到去找你个3接口转两接口的转换器.简单的分析下这个

  • 详解设计模式中的Command命令模式及相关C++实现

    命令模式的作用是将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化:对请求排队或记录请求日志,以及支持可撤销的操作. 由于"行为请求者"与"行为实现者"的紧耦合,使用命令模式,可以对请求排队或记录请求日志,以及支持可撤销的操作. 命令模式把请求一个操作的对象与知道怎么执行一个操作的对象分割开. Command模式关键就是讲一个请求封装到一个类中(Command),再提供处理对象(Receiver),最后Command命令由Invoker激活.另外,我们

  • 详解C++设计模式编程中责任链模式的应用

    职责链模式:使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系.将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止. 其思想很简单,比如考虑员工要求加薪.公司的管理者一共有三级,总经理.总监.经理,如果一个员工要求加薪,应该向主管的经理申请,如果加薪的数量在经理的职权内,那么经理可以直接批准,否则将申请上交给总监.总监的处理方式也一样,总经理可以处理所有请求.这就是典型的职责链模式,请求的处理形成了一条链,直到有一个对象处理请求.给出这个例子的UML图.

  • 详解C++设计模式编程中对状态模式的运用

    状态模式:当一个对象的内在状态发生变化时,允许改变其行为,这个对象看来像是改变了其类. 状态模式与策略模式的UML图几乎一模一样,下面列举了两者的不同: (1)可以通过环境类状态的个数来决定是使用策略模式还是状态模式. (2)策略模式的环境类自己选择一个具体策略类,具体策略类无须关心环境类:而状态模式的环境类由于外在因素需要放进一个具体状态中,以便通过其方法实现状态的切换,因此环境类和状态类之间存在一种双向的关联关系. (3)使用策略模式时,客户端需要知道所选的具体策略是哪一个,而使用状态模式时

  • 设计模式中的备忘录模式解析及相关C++实例应用

    备忘录模式旨在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态.这样以后就可将该对象恢复到原先保存的状态.在命令模式中,备忘录模式经常还经常被用来维护可以撤销(Undo)操作的状态. 类图: Originator:负责创建一个备忘录Memento,用以记录当前时刻它的内部状态,并可使用备忘录恢复内部状态.Originator可根据需要决定Memento存储Originator的哪些内部状态. Memento:负责存储Originator对象的内部状态,并可防止Origin

  • 详解state状态模式及在C++设计模式编程中的使用实例

    每个人.事物在不同的状态下会有不同表现(动作),而一个状态又会在不同的表现下转移到下一个不同的状态(State).最简单的一个生活中的例子就是:地铁入口处,如果你放入正确的地铁票,门就会打开让你通过.在出口处也是验票,如果正确你就可以 ok,否则就不让你通过(如果你动作野蛮,或许会有报警(Alarm),:)). 有限状态自动机(FSM)也是一个典型的状态不同,对输入有不同的响应(状态转移). 通常我们在实现这类系统会使用到很多的 Switch/Case 语句,Case 某种状态,发生什么动作,C

随机推荐