1.概述

在面向对象开发过程中,通常我们会遇到这样的一个问题:我们知道一个算法所需的关键步骤,并确定了这些步骤的执行顺序。但是某些步骤的具体实现是未知的,或者说某些步骤的实现与具体的环境相关。
例子1:银行业务办理流程
在银行办理业务时,一般都包含几个基本固定步骤:
取号排队->办理具体业务->对银行工作人员进行评分。
取号取号排队和对银行工作人员进行评分业务逻辑是一样的。但是办理具体业务是个不相同的,具体业务可能取款、存款或者转账。

2.问题

如何保证架构逻辑的正常执行,而不被子类破坏 ?

3.解决方案

模板方法:定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。
T模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。(Template Method Pattern:Definethe
skeleton of an algorithm in an operation,deferring some steps
tosubclasses.Template Methodletssubclasses redefine certain steps of an
algorithmwithoutchanging the algorithm's structure. )

1)模板方法模式是基于继承的代码复用基本技术,模板方法模式的结构和用法也是面向对象设计的核心之一。在模板方法模式中,可以将相同的代码放在父类中,而将不同的方法实现放在不同的子类中。
2)在模板方法模式中,我们需要准备一个抽象类,将部分逻辑以具体方法以及具体构造函数的形式实现,然后声明一些抽象方法来让子类实现剩余的逻辑。不同的子类可以以不同的方式实现这些抽象方法,从而对剩余的逻辑有不同的实现,这就是模板方法模式的用意。模板方法模式体现了面向对象的诸多重要思想,是一种使用频率较高的模式。

4.适用性

模板方法应用于下列情况:
• 1) 一次性实现一个算法的不变的部分,并将可变的行为留给子类来实现。
• 2)各子类中公共的行为应被提取出来并集中到一个公共父类中以避免代码重复。首先识别现有代码中的不同之处,并且将不同之处分离为新的操作。最后,用一个调用这些新的操作的模板方法来替换这些不同的代码。
• 3)控制子类扩展。模板方法只在特定点调用“ hook”操作 ,这样就只允许在这些点进行扩展。

5.结构

6.模式的组成

AbstractClass(抽象类):定义抽象的原语操作,具体的子类将重定义它们以实现一个算法的各步骤。主要是实现一个模板方法,定义一个算法的骨架。该模板方法不仅调用原语操作,也调用定义在AbstractClass或其他对象中的操作。
ConcreteClass(具体类):实现原语操作以完成算法中与特定子类相关的步骤。
由于在具体的子类ConcreteClass中重定义了实现一个算法的各步骤,而对于不变的算法流程则在AbstractClass的TemplateMethod中完成。

7.效果

模板方法模式的优点:
1)模板方法模式在一个类中形式化地定义算法,而由它的子类实现细节的处理。
2)模板方法是一种代码复用的基本技术。它们在类库中尤为重要,它们提取了类库中的公共行为。
3)模板方法模式导致一种反向的控制结构,这种结构有时被称为“好莱坞法则” ,即“别找我们,,我们找你”通过一个父类调用其子类的操作(而不是相反的子类调用父类),通过对子类的扩展增加新的行为,符合“开闭原则”

模板方法模式的缺点:
每个不同的实现都需要定义一个子类,这会导致类的个数增加,系统更加庞大,设计也更加抽象,但是更加符合“单一职责原则”,使得类的内聚性得以提高。

8.实现

#include <iostream>
using namespace std; class AbstractClass
{
public:
void TemplateMethod()
{
PrimitiveOperation1();
cout<<"TemplateMethod"<<endl;
PrimitiveOperation2();
} protected:
virtual void PrimitiveOperation1()
{
cout<<"Default Operation1"<<endl;
} virtual void PrimitiveOperation2()
{
cout<<"Default Operation2"<<endl;
}
}; class ConcreteClassA : public AbstractClass
{
protected:
virtual void PrimitiveOperation1()
{
cout<<"ConcreteA Operation1"<<endl;
} virtual void PrimitiveOperation2()
{
cout<<"ConcreteA Operation2"<<endl;
}
}; class ConcreteClassB : public AbstractClass
{
protected:
virtual void PrimitiveOperation1()
{
cout<<"ConcreteB Operation1"<<endl;
} virtual void PrimitiveOperation2()
{
cout<<"ConcreteB Operation2"<<endl;
}
}; int main()
{
AbstractClass *pAbstractA = new ConcreteClassA;
pAbstractA->TemplateMethod(); AbstractClass *pAbstractB = new ConcreteClassB;
pAbstractB->TemplateMethod(); if (pAbstractA) delete pAbstractA;
if (pAbstractB) delete pAbstractB;
}

9.与其他相关模式

1)策略模式:模板方法使用继承来改变算法的一部分。 Strategy使用委托来改变整个算法。模板方法模式与策略模式的作用十分类似,有时可以用策略模式替代模板方法模式。模板方法模式通过继承来实现代码复用,而策略模式使用委托,把不确定的行为集中到一个接口中,并在主类委托这个接口。委托比继承具有更大的灵活性。

10.模式的扩展

1)模板方法模式与控制反转(好莱坞原则)在模板方法模式中,子类不显式调用父类的方法,而是通过覆盖父类的方法来实现某些具体的业务逻辑,父类控制对子类的调用,这种机制被称为好莱坞原则(Hollywood Principle),好莱坞原则的定义为:“不要给我们打电话,我们会给你打电话(Don‘t call us, we’ll call you)”。在好莱坞,把简历递交给演艺公司后就只有回家等待。由演艺公司对整个娱乐项的完全控制,演员只能被动式的接受公司的差使,在需要的环节中,完成自己的演出。模板方法模式充分的体现了“好莱坞”原则。由父类完全控制着子类的逻辑,子类不需要调用父类,而通过父类来调用子类,子类可以实现父类的可变部份,却继承父类的逻辑,不能改变业务逻辑。

2)模板方法模式符合开闭原则

模板方法模式意图是由抽象父类控制顶级逻辑,并把基本操作的实现推迟到子类去实现,这是通过继承的手段来达到对象的复用,同时也遵守了开闭原则。

父类通过顶级逻辑,它通过定义并提供一个具体方法来实现,我们也称之为模板方法。通常这个模板方法才是外部对象最关心的方法。在上面的银行业务处理例子中,templateMethodProcess这个方法才是外部对象最关心的方法。所以它必须是public的,才能被外部对象所调用。

子类需要继承父类去扩展父类的基本方法,但是它也可以覆写父类的方法。如果子类去覆写了父类的模板方法,从而改变了父类控制的顶级逻辑,这违反了“开闭原则”。我们在使用模板方法模式时,应该总是保证子类有正确的逻辑。所以模板方法应该定义为final的。所以AbstractClass类的模板方法templateMethodProcess方法应该定义为final。

模板方法模式中,抽象类的模板方法应该声明为final的。因为子类不能覆写一个被定义为final的方法。从而保证了子类的逻辑永远由父类所控制。

3)模板方法模式与对象的封装性

面向对象的三大特性:继承,封装,多态。

对象有内部状态和外部的行为。封装是为了信息隐藏,通过封装来维护对象内部数据的完整性。使得外部对象不能够直接访问一个对象的内部状态,而必须通过恰当的方法才能访问。

对象属性和方法赋予指定的修改符(public、protected、private)来达到封装的目的,使得数据不被外部对象恶意的访问及方法不被错误调用导造成破坏对象的封装性。

降低方法的访问级别,也就是最大化的降低方法的可见度是一种很重要的封装手段。最大化降低方法的可见度除了可以达到信息隐藏外,还能有效的降低类之间的耦合度,降低一个类的复杂度。还可以减少开发人员发生的的错误调用。

一个类应该只公开外部需要调用的方法。而所有为public方法服务的方法都应该声明为protected或private。如是一个方法不是需要对外公开的,但是它需要被子类进行扩展的或调用。那么把它定义为protected.否则应该为private。

显而易见,模板方法模式中的声明为abstract的基本操作都是需要迫使子类去实现的,它们仅仅是为模板方法服务的。它们不应该被抽象类(AbstractClass)所公开,所以它们应该protected。

因此模板方法模式中,迫使子类实现的抽象方法应该声明为protected abstract。

4)模板方法与勾子方法(hookMethod)

模板方法模式的抽象类定义方法:

模板方法:一个模板方法是定义在抽象类中的、把基本操作方法组合在一起形成一个总算法或一个总行为的方法。
基本方法:基本方法是实现算法各个步骤的方法,是模板方法的组成部分。基本方法如下:
抽象方法(Abstract Method)
•具体方法(Concrete Method)
•钩子方法(Hook Method):“挂钩”方法和空方法,
hook方法在抽象类中的实现为空,是留给子类做一些可选的操作。如果某个子类需要一些特殊额外的操作,则可以实现hook方法,当然也可以完全不用理会,因为hook在抽象类中只是空方法而已。
1)钩子方法的引入使得子类可以控制父类的行为。
2)最简单的钩子方法就是空方法,也可以在钩子方法中定义一个默认的实现,如果子类不覆盖钩子方法,则执行父类的默认实现代码。
3)比较复杂一点的钩子方法可以对其他方法进行约束,这种钩子方法通常返回一个boolean类型,即返回true或false,用来判断是否执行某一个基本方法。由子类来决定是否调用hook方法。

11.总结与分析

1)模板方法模式是一种类的行为型模式,在它的结构图中只有类之间的继承关系,没有对象关联关系。

2)板方法模式是基于继承的代码复用基本技术,模板方法模式的结构和用法也是面向对象设计的核心之一。在模板方法模式中,可以将相同的代码放在父类中,而将不同的方法实现放在不同的子类中。
3)在模板方法模式中,我们需要准备一个抽象类,将部分逻辑以具体方法以及具体构造函数的形式实现,然后声明一些抽象方法来让子类实现剩余的逻辑。不同的子类可以以不同的方式实现这些抽象方法,从而对剩余的逻辑有不同的实现,这就是模板方法模式的用意。模板方法模式体现了面向对象的诸多重要思想,是一种使用频率较高的模式。

Template Method - 模板方法模式的更多相关文章

  1. 设计模式13:Template Method 模板方法模式(行为型模式)

    Template Method 模板方法模式(行为型模式) 变与不变 变化——是软件永恒的主题,如何管理变化带来的复杂性?设计模式的艺术性和复杂度就在于如何分析,并发现体系中的变化点和稳定点,并使用特 ...

  2. 设计模式 : Template method 模板方法模式 -- 行为型

      设计模式中,模板模式面向的是方法级别的流程.(不过好像世界上大部分问题,都可以抽象点.抽象点吧,最后抽象到一个方法里面吧.) 1. 一个方法,可以用来描述一个流程,这个流程涉及多个环节,不同环节可 ...

  3. 设计模式(22)--Template Method(模板方法模式)--行为型

    作者QQ:1095737364    QQ群:123300273     欢迎加入! 1.模式定义: 模板方法模式是类的行为模式.准备一个抽象类,将部分逻辑以具体方法以及具体构造函数的形式实现,然后声 ...

  4. 设计模式学习笔记——Template Method模板方法模式

    可能是最简单的设计模式. 而且你我都用过而不自知. 因为,模板方法模式也者,就是面向对象中的继承.公用部分放在父类,子类继承父类,然后扩展.呵呵.

  5. 设计模式C++学习笔记之九(Template Method模板方法模式)

      模板模式也是相当简单的一种模式,而且是比较常用的.模板模式是定义一个操作中的算法的骨架,而将一些步骤延迟到子类中.TemplateMethod使得子类可以不改变一个算法的结构即可重定义该算法的某些 ...

  6. 设计模式的征途—17.模板方法(Template Method)模式

    在现实生活中,很多事情都需要经过几个步骤才能完成,例如请客吃饭,无论吃什么,一般都包含:点单.吃东西.买单等几个步骤,通常情况下这几个步骤的次序是:点单=>吃东西=>买单.在这3个步骤中, ...

  7. Template Method 模板方法

      简介 定义一个操作中的算法的骨架,而将一些步骤延迟到子类中. 模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤的细节 抽象模板AbstractClass的方法分为两类: 基本 ...

  8. Template Method 模板方法 MD

    Markdown版本笔记 我的GitHub首页 我的博客 我的微信 我的邮箱 MyAndroidBlogs baiqiantao baiqiantao bqt20094 baiqiantao@sina ...

  9. 【java设计模式】之 模板方法(Template Method)模式

    1. 模板方法的一个实例 这一节主要来学习一下设计模式中的模板方法模式.我们先来看一个例子:假如现在老板让你做一个汽车的模型,要求只要完成基本功能即可,不考虑扩展性,那你会怎么做呢?我们首先会根据经验 ...

随机推荐

  1. PL/SQL Developer连接64位Oracle

    在64位系统上安装64位的Oracle数据库,但是没有对应的64位PL/SQL Developer,此时就不能使用PL/SQL Developer来进行直接连接的,所以要想实现连接还得需要其他途径来完 ...

  2. Composite 组合模式 树 递归 MD

    Markdown版本笔记 我的GitHub首页 我的博客 我的微信 我的邮箱 MyAndroidBlogs baiqiantao baiqiantao bqt20094 baiqiantao@sina ...

  3. .NET-WEB网站部署的过程中需要注意的问题

    --部署的网站的文件夹需要有IIS_USER权限(写权限) --部署的网站在外网不能访问:一般是由于防火墙的限制导致的.配置防火墙就可以.

  4. Learning How To Code Neural Networks

    原文:https://medium.com/learning-new-stuff/how-to-learn-neural-networks-758b78f2736e#.ly5wpz44d This i ...

  5. idea丢失svn解决办法

    今天打开Idea,习惯用ctrl+t来更新svn,杯具出现了,快捷键失效了,我觉得可能是其他的什么软件占用了这个快捷键,于是把qq,微信,rtx,各种软件都关掉,发现还是不好使,于是重启了一下,发现还 ...

  6. 一个用于发送HTML格式邮件的类

    以下类是在网上孙钰佳的版本上改写而来,主要变化了三点:1.去掉了附件部分:2.形式从纯Java类改成可注入方式:3.to,cc和bcc都变成了一堆人,以前是一个人. 以下是Java类的代码: impo ...

  7. 如何使用千千静听为MP3添加专辑封面和文字信息

    使用千千静听播放器打开某MP3文件,右击该文件,选择属性. 2 点击专辑封面即可添加或更换专辑封面 点击保存到文件再点击重新读取文件即可发现有效了 3 为MP3批量添加添加封面 选中播放列表的所有文件 ...

  8. STL - 容器 - Deque

    Deque和Vector类似,只不过deque头尾都开放,能够在头尾进行快速插入和删除操作 DequeTest.cpp #include <iostream> #include <d ...

  9. ZH奶酪:在博客中添加Latex公式

    1. 点击编辑器中的插入图片: 2.在URL输入下边的地址: http://latex.codecogs.com/gif.latex?你的latex代码 就可以了-

  10. ZH奶酪:VirtualBox虚拟机与主机ping不通

    6.进入虚拟机的Ubuntu(以下简称VBUbuntu),在VBUbuntu中用ifconfig查看ip地址,在Windows7中用ipconfig查看ip地址. 在VBUbuntu中ping Win ...