详解设计模式中的模板方法模式及在C++中的使用

模板方法模式是设计模式行为型中最简单的一种设计模式。在实际中你甚至可能经常用到,只是你自己不知道它是一种设计模式罢了。
模板方法模式定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
角色:
抽象类(AbstractClass): 定义抽象的原语操作,具体的子类将重定义它们以实现一个算法,实现一个模板方法,定义一个算法的骨架。该模板方法不仅调用原语操作,也调用定义
具体子类 (ConcreteClass): 实现原语操作以完成算法中与特定子类相关的步骤。
UML图:

示例:假如你是一个老师,现在你要给你的学生出一份期末考试试卷。你班上有几十个学生,你将考虑如何为设计考试卷。
  经分析显然学生的试卷大部分类容都是一致的,唯一不一致的是姓名和答案。老师设计好试卷,只需要把试卷交个学生填写答案即可。学生不需要把题目照抄一份。
所以我们需要把试卷抽象成基类,并且给学生留下填写答案以及姓名的地方。

class TestPaper
{
public:
  void DoTestPaper(){
    StudentName();
    TestTitleOne();
    TestTitleTwo();
  }; 

  void TestTitleOne(){
    cout<<"题目一:X国的房价会降下来么?"<<endl;
    AnswerOne();
  } 

  void TestTitleTwo(){
    cout<<"题目二:说说你的新闻联播的看法?"<<endl;
    AnswerTwo();
  } 

  virtual void AnswerOne() = 0;
  virtual void AnswerTwo() = 0;
  virtual void StudentName() = 0;
};

显然,上面 AnswerOne, AnserTwo,StudentName 就是学生答题的地方,学生不需要把题目也抄下来。只需要实现我们的这三个方法就可以了。
例如:小红的试卷

class XiaoHongTestPaper : public TestPaper
{
public:
  void StudentName(){
    cout<<"姓名:小红"<<endl;
  }
  void AnswerOne(){
    cout<<"答:相信X,相信国家,明年一定降下来。"<<endl<<endl;
  }
  void AnswerTwo(){
    cout<<"答:新闻联播是我最喜欢的节目啊。"<<endl<<endl;
  }
};

小张的试卷:

class XiaoZhangTestPaper : public TestPaper
{
public:
  void StudentName(){
    cout<<"姓名:小张"<<endl;
  }
  void AnswerOne(){
    cout<<"答:呵呵,还是去做你的X国梦吧。"<<endl<<endl;
  }
  void AnswerTwo(){
    cout<<"答:我很幸福"<<endl<<endl;
  }
};

客户端:

int main(int argc, char* argv[])
{
  XiaoHongTestPaper paper1;
  paper1.DoTestPaper(); 

  XiaoZhangTestPaper paper2;
  paper2.DoTestPaper(); 

  system("pause");
  return 0;
}

关于模板方法的讨论

模板方法模式是很简单模式,但是也应用很广的模式。如上面的分析和实现中阐明的模板方法是采用继承的方式实现算法的异构,其关键点就是将通用算法封装在抽象基类中,并将不同的算法细节放到子类中实现。

模板方法模式获得一种反向控制结构效果,这也是面向对象系统的分析和设计中一个原则 DIP(依赖倒置:Dependency Inversion Principles)。其含义就是父类调用子类的操作(高层模块调用低层模块的操作),低层模块实现高层模块声明的接口。这样控制权在父类(高层模块),低层模块反而要依赖高层模块。

继 承 的 强 制 性 约 束 关 系 也 让模板方法模 式 有 不 足 的 地 方 , 我 们 可 以 看 到 对 于ConcreteClass 类中的实现的原语方法 Primitive1(),是不能被别的类复用。假设我们要创建一个 AbstractClass 的变体 AnotherAbstractClass,并且两者只是通用算法不一样,其原语操作想复用 AbstractClass 的子类的实现。但是这是不可能实现的,因为 ConcreteClass 继承自AbstractClass,也就继承了 AbstractClass 的通用算法,AnotherAbstractClass 是复用不了ConcreteClass 的实现,因为后者不是继承自前者。

模板方法模式暴露的问题也正是继承所固有的问题,策略模式则通过组合(委托)来达到和模板方法模式类似的效果,其代价就是空间和时间上的代价,关于策略模式的详细讨论请参考 Strategy 模式解析。

(0)

相关推荐

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

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

  • 深入解析C++编程中对设计模式中的策略模式的运用

    策略模式也是一种非常常用的设计模式,而且也不复杂.下面我们就来看看这种模式. 定义:策略模式定义了一系列的算法,并将每一个算法封装起来,而且使它们还可以相互替换.策略模式让算法独立于使用它的客户而独立变化. 角色: 抽象策略角色(Strategy): 抽象策略类. 具体策略角色(ConcreteStrategy):封装了继续相关的算法和行为. 环境角色(Context):持有一个策略类的引用,最终给客户端调用. UML图: 例子: #include <iostream> using names

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

    准备一个抽象类,将部分逻辑以具体方法以及具体构造子的形式实现,然后声明一些抽象方法来迫使子类实现剩余的逻辑.不同的子类可以以不同的方式实现这些抽象方法,从而对剩余的逻辑有不同的实现.这就是模版方法模式的用意. 很多人可能没有想到,模版方法模式实际上是所有模式中最为常见的几个模式之一,而且很多人可能使用过模版方法模式而没有意识到自己已经使用了这个模式.模版方法模式是基于继承的代码复用的基本技术,模版方法模式的结构和用法也是面向对象设计的核心. 模版方法模式需要开发抽象类和具体子类的设计师之间的协作

  • C++设计模式编程中Facade外观模式的使用实例解析

    外观模式提供了一个统一的接口,用来访问子系统的一群接口.外观定义了一个高层接口,让子系统更容易使用.外观模式让接口变得简单,简化了子系统的接口.外观模式十分简单,简而言之,就是简化你的类的接口,将一系列的复杂的过程封装到内部,对外只提供最简单的接口. 结构图: 适用场景: 当你要为一个复杂子系统提供一个简单接口时.子系统往往因为不断演化而变得越来越复杂.大多数模式使用时都会产生更多更小的类.这使得子系统更具可重用性,也更容易对子系统进行定制,但这也给那些不需要定制子系统的用户带来一些使用上的困难

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

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

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

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

  • C++设计模式编程中的迭代器模式应用解析

    迭代器模式:提供一种方法顺序访问一个聚合对象中个各个元素,而不暴露该对像的内部表示. 迭代器模式应该是最为熟悉的模式了,最简单的证明就是我在实现组合模式.享元模式.观察者模式中就直接用到了 STL 提供的迭代器来遍历 Vector 或者 List数据结构. 迭代器模式也正是用来解决对一个聚合对象的遍历问题,将对聚合的遍历封装到一个类中进行,这样就避免了暴露这个聚合对象的内部表示的可能. 模式的动机: (1)一个聚合对象,如一个列表(List)或者一个集合(Set),应该提供一种方法来让别人可以访

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

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

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

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

  • C++设计模式之桥接模式

    问题描述 现在要去画一个图形,图形有长方形.圆形和扇形等等:而图形又可以加上不同的颜色,然后,我们就可以画出红色的长方形,绿色的长方形:红色的圆形,绿色的圆形等等.而这种图形的形状在变化,图形的颜色也在变化,当使用代码去实现时,如何面对这种多方面的变化呢?这就要说到今天的桥接模式了. 什么是桥接模式? 对于上述的图形与颜色的问题时,很多时候,我们让各个图形类继承颜色类,比如: 复制代码 代码如下: class CShape { }; class CRectangle : public CShap

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

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

随机推荐