模板方法模式实现了分离可变和不变的部分,并把可变的部分封装起来。
在抽象类中定义一个总体框架,包含可变的和不可变的模块,然后将可变模块的实现延迟到子类中实现。使得子类可以在不改变框架结构的情况下,重新定义框架中某些模块的具体实现。
这也符合OO设计的几个重要原则:
封装变化 在这里指被抽向成虚函数的可变部分。
针对接口编程而不针对实现编程 框架定义在父类而不是具体的某个子类中。父类中的可变方法的实现不在父类,而是在各个子类。程序在运行时可以动态改变所指向的子类类型。
晚绑定代替早绑定
晚绑定和早绑定
把函数体和函数调用相联系称为绑定。类中的一般的函数都是早绑定,也就是在调用的地方,已经把要调用的函数确定好了,调用的类就是定义它的类。
模板方法模式中,针对不可变部分使用的就是早绑定策略:父类指针不管指向哪个子类,调用到的永远都是父类中定义号的方法,也就是不可变。
而针对可变部分则使用了晚绑定策略:将父类方法声明为virtual函数,调用该方法时,根据其指向的对象找vtpr指针,然后找vptr中找其具体的子类实现,也就是可变。
应用实例
一个简单的例子,炒两道菜,第一步洗锅,第二部倒油,第三部放食材,第四部放调料,第五步翻炒装盘。其中一二五步是一样的操作,但是A菜和B菜所用的食材和调料是不一样的,所以定义一个抽象类将炒菜的过程抽象出来。
class Base {
public:
//早绑定,不希望子类覆盖这个方法
void cookProcess()
{
//第一步:洗锅
washPot();
//第二步:倒油
addOil();
//第三步:放食材
addVegetable();
//第四步:倒调料
addSauce();
//第五步:翻炒装盘
fry();
}
protected:
//决定结构里早绑定和晚绑定
//第一步:洗锅是一样的,直接实现
void washPot() {
std::cout << "第一步:洗锅" << endl;
}
//第二步:倒油是一样的,直接实现
void addOil() {
std::cout << "第二步:倒油" << endl;
}
//第三步:放食材是不一样的,声明为虚方法,具体实现由子类进行覆盖
virtual void addVegetable() = 0;
//第四步:倒调料是不一样的,声明为虚方法,具体实现由子类进行覆盖
virtual void addSauce() = 0;
//第五步:翻炒装盘是一样的,所以直接实现
void fry() {
cout << "第五步:翻炒然后装盘" <<endl;
}
};
针对不同的菜品实现不同的烹饪步骤。
class cookA : public Base
{
protected:
void addVegetable() {
cout << "第三步:炒A菜:放A菜食材" << endl;
}
void addSauce() {
cout << "第四步:炒A菜:放A菜的调料" << endl;
}
};
class cookB : public Base
{
protected:
void addVegetable() {
cout << "第三步:炒B菜:放B菜食材" << endl;
}
void addSauce() {
cout << "第四步:炒B菜:放B菜的调料" << endl;
}
};
在程序中改变炒菜父类的指向的子类对象即可实现不同菜品的烹饪。
int main()
{
Base *p1 = new cookA;
Base *p2 = new cookB;
p1->cookProcess();
p2->cookProcess();
delete p1;
delete p2;
p1 = NULL;
p2 = NULL;
return 0;
}