一、概述
Template Method(模板方法)模式定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。Template Method使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
这里所说的Template跟Generic Programming(范型编程)中讨论的C++的template不是一回事(虽然有一定的相似性),C++的template是一种逻辑复用的方式,它可以不依赖于OO的Inheritance(继承)机制独立存在,因为GP跟OO所讨论的是完全不同的两个方面,虽然二者经常被融合在一起使用;Template Method模式与template不同,它是建立在继承机制 + 虚函数基础上的,它的核心在于在基类中定义好逻辑处理的框架(或称完成一项任务所需依次执行的步骤,或一段通用的处理逻辑),将具体的处理细节交给子类具体实现,从而达到“使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤”的目的。
二、结构
Template Method模式的结构如下图所示:
图1、Template Method模式类图示意
其中的参与者比较简单:
1.AbstractClass(抽象类):定义一到多个抽象方法(也可以不是抽象方法,但至少应该是virtual方法。视你的应用需要,如果你的AbstractClass负责实现一个通用版本的算法,各子类对该方法进行进一步细化,则只需定义成virtual方法即可),具体的子类将重定义它们以实现一个算法;而且还实现一个模板方法,来定义一个算法的骨架。该模板方法不仅调用前面的抽象方法,也可以调用其他的操作。各抽象方法往往被定义成protected(保护)成员,以保证它们只被模板方法调用,而TemplateMethod往往被定义成public非虚成员函数。
2.ConcreteClass(具体类):实现父类中的抽象方法以完成算法中与特定子类相关的步骤。
这里的关键是基类中的TemplateMethod方法,因为正是它定义了对各子类对象适用的通用的处理逻辑。
三、应用
Template Method模式是一个使用频率比较高的模式,因为对于同一种类型的对象而言,他们之间一些处理流程往往是一致的,对象之间的差异仅在于具体的处理逻辑,因此,可以将通用的逻辑提取出来放到AbstractClass中实现,而将实现的具体细节交给子类完成。
从这一点上讲,Template Method与Strategy模式存在一定的相似性,但Template Method中实现的主体是ConcreteClass,AbstractClass仅定义了接口和希望子类重新定义的方法,通过继承来改变算法;而Strategy模式中Context类与Strategy类之间不存在继承关系,体现的是一种委托的关系。
四、优缺点
五、举例
MFC作为一个典型的应用程序框架,可以从中找到一些应用Template Method的例子,如:
void CView::OnPaint() // Template Method
{
// standard paint routine
CPaintDC dc(this);
OnPrepareDC(&dc);
OnDraw(&dc);
}
void CView::OnDraw(CDC*) // default implementation is empty, subclass always need override it.
{
}
void CView::OnPrepareDC(CDC* pDC, CPrintInfo* pInfo) // provide a basic implementation
{
ASSERT_VALID(pDC);
UNUSED(pDC); // unused in release builds
// Default to one page printing if doc length not known
if (pInfo != NULL)
pInfo->m_bContinuePrinting =
(pInfo->GetMaxPage() != 0xffff || (pInfo->m_nCurPage == 1));
}
上面的OnPaint就有几分Template Method模式的意思。
下面是一个运用Template Method进行排序的例子,基本的排序逻辑在基类Sort中实现,但具体的排序方法(升序还是降序)由子类决定:
// "Template Method defines an algorithm in terms of abstract operations that subclasses
// override to provide concrete behavior." Here, doIt() is the algorithm, and needSwap() is the abstract operation.
#include <stdlib.h>
#include <time.h>
#include <iostream>
using namespace std;
class Sort { ////// Shell sort //////
public:
void doIt( int v[], int n )
{
for (int g = n/2; g > 0; g /= 2)
for (int i = g; i < n; i++)
for (int j = i-g; j >= 0; j -= g)
if (needSwap(v[j], v[j+g]))
doSwap(v[j], v[j+g]);
}
private:
virtual int needSwap(int,int) = 0;
void doSwap(int& a,int& b)
{
int t = a; a = b; b = t;
}
};
class SortUp : public Sort
{
int needSwap(int a, int b)
{
return (a > b);
}
};
class SortDown : public Sort
{
int needSwap(int a, int b)
{
return (a < b);
}
};
void main( void )
{
const int NUM = 10;
int array[NUM];
time_t t;
srand((unsigned) time(&t));
for (int i=0; i < NUM; i++)
{
array[i] = rand() % 10 + 1;
cout << array[i] << ' ';
}
cout << endl;
SortUp upObj;
upObj.doIt( array, NUM );
for (int u=0; u < NUM; u++)
cout << array[u] << ' ';
cout << endl;
SortDown downObj;
downObj.doIt( array, NUM );
for (int d=0; d < NUM; d++)
cout << array[d] << ' ';
cout << endl;
}