1.场景分析
茶和咖啡是两种饮料,但是它们的冲泡方法十分相似,冲泡方法如下。
茶:把水煮沸->沸水浸泡茶叶->把茶倒进杯子->加柠檬
咖啡:把水煮沸->用沸水冲泡咖啡->把咖啡装进杯子->加糖和牛奶
用代码来实现咖啡和茶,如下:
class Coffee
{
public:
//准备一杯咖啡
void prepareRecipe()
{
boilWater();
brewCofferGrainds();
pourInCup();
addSugarAndMilk();
}
void boilWater();
void brewCofferGrainds();
void pourInCup();
void addSugarAndMilk();
};
class Tea
{
public:
//准备一杯茶
void prepareRecipe()
{
boilWater();
steepTeaBag();
pourInCup();
addLemon();
}
void boilWater();
void steepTeaBag();
void pourInCup();
void addLemon();
};
现在我们从咖啡和茶中抽象出一个基类:咖啡因饮料(CaffeineBeverage)。我们将咖啡的brewCoffeeGrinds()和茶的steepTeaBag()函数抽象为一个函数brew(),将addSugarAndMilk()和addLemon()抽象为addCondiments(),写出基类如下:
#include <iostream>
using namespace std;
class CaffieineBeverage
{
public:
void prepareRecipe()
{
boilWater();
brew();
pourInCup();
addCondiments();
}
void boilWater();
virtual void brew() = 0;
void pourInCup();
virtual void addCondiments() = 0;
};
class Coffee : public CaffieineBeverage
{
public:
void brew() override
{
cout << "brew coffee grinds" << endl;
}
void addCondiments() override
{
cout << "add milk and sugar" << endl;
}
};
class Tea : public CaffieineBeverage
{
public:
void brew() override
{
cout << "steep tea bag" << endl;
}
void addCondiments() override
{
cout << "add lemon" << endl;
}
};
上面我们实现的CaffieineBeverage类便实现了模板方法。模板方法定义了一个算法的步骤,并允许子类为一个或多个步骤提供实现。比如我们提供了prepareRecipe()的模板步骤,而其中有两个步骤brew()、addCondiments()为虚函数,其可以供其子类自定义。
2.意图
模板方法模式在一个方法中定义一个算法的骨架,而算法中的一些步骤则延迟到子类中。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤。
3.实用性
- 一次性实现一个算法的不变的部分,并将可变的行为留给子类来实现。
- 各子类中的公共行为被提取出来并集中到一个公共父类中以避免代码重复。
- 可以通过钩子(hook)控制子类扩展。
4.结构
5.效果
模板方法是一种代码复用的基本技术。它们在类库中尤为重要,他们提取了类库中的公共方法。模板方法导致了一种反向的控制结构,即父类调用一个子类的操作,而不是相反。
模板方法中经常用到钩子(hook)操作。钩子操作缺省通常是一个空操作,其可以在子类中进行扩展。模板方法应该指明哪些操作是钩子操作(可以被重定义)以及哪些操作是抽象操作(必须被重定义)。
6.实现
有三个实现问题值得注意:
1>使用c++访问控制。一个模板方法调用的原语操作可以定义为保护成员,这保证它们只能被模板方法调用。
2>尽量减少原语操作。子类需要重定义的操作越多,客户程序就越冗长。
3>命名约定。可以给应被重定义的那些操作的名字加上一个前缀以识别它们。
7.代码示例
class View
{
public:
void display()
{
setFocus();
DoDisplay();
ResetFocus();
}
protected:
void setFocus();
void ResetFocus();
virtual void DoDisplay() {}
};
class MyView : public View
{
protected:
void DoDisplay() override
{
cout << "render the view's contents" << endl;
}
};
绘图类View,其只在获得焦点时才进行绘图。其定义了一个显式的模板方法,并提供了一个钩子DoDisplay()来让子类扩展绘图的内容与样式。