模式定义
定义一个操作中的算法骨架,而将一些步骤延迟到子类中。TemplateMethod
使得子类可以不改变一个算法的接口即可重定义改算法的某些特定步骤。
模式动机
- 一个模板方法用一些抽象的操作定义一个算法,而子类将重新定义这些操作以提供具体的行为。
- 通常这种情况下都是一些相同的功能,执行相同的代码顺序,有点类似 C 语言的面向过程编程,父类规定程序运行的逻辑,子类负责实现具体的算法功能。
相关实例
- 例如在泡茶与煮咖啡相比,抽象来看它们都需要经过如步骤:
烧水 --> 水里加想喝东西 --> 倒入杯中 --> 加一些辅料
这样的顺序是不会改变的称为模板,但是改变的可能是每一个步骤中的东西,例如烧水
可以是自来水,可以是山泉水等等,想喝的东西
,可以是咖啡粉,茶叶,奶茶粉等等,它们取决于子类如何实现。
模式结构
- AbstractClass:抽象类
- ConcreteClass:具体类
代码分析
如上述喝饮料的例子,实现代码如下
#include <iostream>
using namespace std;
class DrinkTemplate { //抽象的饮料类
protected:
virtual void BuildWater() = 0; //要求子类实现的相关功能
virtual void Brew() = 0;
virtual void PourInCup() = 0;
virtual void AddSonmething() = 0;
public:
void Make() { //模板的算法
BuildWater();
Brew();
PourInCup();
AddSonmething();
}
};
class Coffee :public DrinkTemplate { //子类实现
private:
virtual void BuildWater() { cout << "烧水..." << endl; }
virtual void Brew() { cout << "煮咖啡..." << endl; }
virtual void PourInCup() { cout << "倒入杯中..." << endl; }
virtual void AddSonmething() { cout << "加糖加牛奶..." << endl; }
};
class Tea :public DrinkTemplate {
private:
virtual void BuildWater() { cout << "烧水..." << endl; }
virtual void Brew() { cout << "泡茶..." << endl; }
virtual void PourInCup() { cout << "倒入杯中..." << endl; }
virtual void AddSonmething() { cout << "什么也不加..." << endl; }
};
int main() {
DrinkTemplate* drink = new Coffee;
drink->Make(); //直接调用父类的模板算法即可
delete drink;
drink = new Tea;
drink->Make();
drink = nullptr;
return 0;
}
模板方法模式的优缺点
优点
- 重用代码,模板方法通过把不变的行为搬移到父类,去除了子类中的重复代码
- 子类实现算法的细节,有助于算法的扩展
- 通过一个父类调用子类实现的操作,通过子类扩展增加新的行为,符合开闭原则
缺点
- 模板方法子类执行的结果影响了父类的结果,会增加代码的阅读难度
适用环境
- 需要固定的算法骨架,实现一个公共部分,将可变的部分交给子类去实现
- 各个子类中具有公共部分,应该抽取出来,集中在一个类中去实现,从而避免代码重复
- 需要控制子类扩展情况,模板方法模式会在特定的点来调用子类的方法,这样只允许在这些点进行扩展