一、引子
这是一个很简单的模式,却被非常广泛的使用。之所以简单是因为在这个模式中仅仅使用到了继承关系。
继承关系由于自身的缺陷,被专家们扣上了“罪恶”的帽子。“使用委派关系代替继承关系”,“尽量使用接口实现而不是抽象类继承”等等专家警告,让我们这些菜鸟对继承“另眼相看”。
其实,继承还是有很多自身的优点所在。只是被大家滥用的似乎缺点更加明显了。合理的利用继承关系,还是能对你的系统设计起到很好的作用的。而模板方法模式就是其中的一个使用范例。
二、定义
模板方法( Template Method)模式:定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。这里的算法的结构,可以理解为你根据需求设计出来的业务流程。特定的步骤就是指那些可能在内容上存在变数的环节。
可以看出来,模板方法模式也是为了巧妙解决变化对系统带来的影响而设计的。使用模板方法使系统扩展性增强,最小化了变化对系统的影响。这一点,在下面的举例中可以很明显的看出来。
三、结构
来看下这个简单模式的结构吧:
1) 抽象类( Abstract Class):定义了一到多个的抽象方法,以供具体的子类来实现它们;而且还要实现一个模板方法,来定义一个算法的骨架。该模板方法不仅调用前面的抽象方法,也可以调用其他的操作,只要能完成自身的使命。
2) 具体类( Concrete Class):实现父类中的抽象方法以完成算法中与特定子类相关的步骤。
四、实现
#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;
}
五、适用情况
根据上面对定义的分析,以及例子的说明,可以看出模板方法适用于以下情况:
1) 一次性实现一个算法的不变的部分,并将可变的行为留给子类来实现。
2) 各子类中公共的行为应被提取出来并集中到一个公共父类中以避免代码重复。其实这可以说是一种好的编码习惯了。
3) 控制子类扩展。模板方法只在特定点调用操作,这样就只允许在这些点进行扩展。
可以看出,使用模板方法模式可以将代码的公共行为提取出来,达到复用的目的。而且,在模板方法模式中,是由父类的模板方法来控制子类中的具体实现。这样你在实现子类的时候,根本不需要对业务流程有太多的了解。