目录
一、引言
老样子,先来看一个具体的简单例子。
冲饮料的固定流程:
- 把水烧开
- 冲泡(咖啡 / 茶不一样)
- 倒入杯子
- 加配料(糖 / 牛奶 / 柠檬不一样)
其中,1、3步骤是固定的,不管冲什么饮料都一样。2、4步骤是可变的,取决于要冲什么饮料。
整个冲饮料的顺序就是模板方法。现在不理解没关系,看完你就明白了。
二、优化前的代码
用代码实现上面的例子。
cpp
#include <iostream>
#include <string>
class Drink {
public:
Drink(const std::string& name):_name(name) {}
void makeDrink() {
boilWater(); // 烧水
brew(); // 冲泡
pourInCup(); // 倒入杯子中
addCondiments(); // 添加配料
}
private:
void boilWater() {
std::cout << "烧水!"<< std::endl;
}
void brew() {
if (_name == "茶") {
std::cout << "冲茶!" << std::endl;
}else if (_name == "咖啡") {
std::cout << "冲咖啡!" << std::endl;
}
}
void pourInCup() {
std::cout << "倒入杯子中!" << std::endl;
}
void addCondiments() {
if (_name == "茶") {
std::cout << "加白糖!" << std::endl;
}else if (_name == "咖啡") {
std::cout << "加牛奶!" << std::endl;
}
}
private:
std::string _name;
};
int main() {
Drink* d1 = new Drink("茶");
d1->makeDrink();
Drink* d2 = new Drink("咖啡");
d2->makeDrink();
delete d1;
delete d2;
return 0;
}
这个代码有什么问题?
如果要冲罗汉果茶,那就要修改Drink类中的代码,在brew()函数和addCondiments()函数中增加一个else if。这就违背了开闭原则------对扩展开放,对修改关闭。
仅凭这一点,这段代码就不是一段好代码。
三、模板方法模式
我们来看看它的定义:
定义一个操作中的算法骨架,而将一些步骤延迟到子类中实现。模板方法使得子类可以不改变一个算法的结构,即可重新定义该算法的某些特定步骤。
是不是感觉说了跟没说一样?
这个毕竟是教科书上的标准定义,抽象很正常。
结合结构图,重新解释这个定义。

在抽象类中定义一个模板方法,这个模板方法规定了业务流程的执行顺序与骨架。对应到上面代码中,makeDrink方法就是模板方法,模板方法中调用的所有函数,叫做基本方法。
流程中的有些步骤,可以声明为抽象方法,具体的实现交给子类重写实现。
子类通过继承抽象类、实现抽象方法,来完成流程的个性化。
对应到上面代码中,brew()函数和addCondiments()函数就可以声明为抽象方法。具体要冲什么饮料、加什么配料由子类来具体实现。
总的来说就是,父类的模板方法定流程骨架,子类通过重写填步骤细节,使得同一套流程可以有不同的结果。
四、优化后的代码
下面,应用模板方法模式对代码进行优化。
cpp
#include <iostream>
#include <string>
class Drink {
public:
// 流程是固定死的
void makeDrink() {
boilWater(); // 烧水
brew(); // 冲泡
pourInCup(); // 倒入杯子中
addCondiments(); // 添加配料
}
protected:
// 这些流程,不管是冲什么,都是不变的,所以放在父类实现
void boilWater() {
std::cout << "烧水!"<< std::endl;
}
void pourInCup() {
std::cout << "倒入杯子中!" << std::endl;
}
// 声明为纯虚函数,由子类实现
virtual void brew() = 0;
virtual void addCondiments() = 0;
};
class DrinkTea : public Drink {
private:
virtual void brew() override {
std::cout << "泡茶" << std::endl;
}
virtual void addCondiments() override {
std::cout << "加白糖" << std::endl;
}
};
class DrinkCoffee : public Drink {
private:
virtual void brew() override {
std::cout << "冲咖啡" << std::endl;
}
virtual void addCondiments() override {
std::cout << "加牛奶" << std::endl;
}
};
int main() {
std::cout << "=========================制作茶========================:" << std::endl;
Drink* d1 = new DrinkTea();
d1->makeDrink();
std::cout << "=========================制作咖啡========================:" << std::endl;
Drink* d2 = new DrinkCoffee();
d2->makeDrink();
delete d1;
delete d2;
return 0;
}

如果要制作罗汉果茶,那么只需要增加一个子类继承父类并实现抽象方法即可。
我们看看优化后的代码都符合哪些设计原则。
- 开闭原则:制作新的饮料不改父类的代码。
- 单一职责:只负责制作饮料。
这两个设计原则是比较容易看出来的。
五、应用场景
流程固定,但流程的中方法可能不一样,这样的场景下就可以考虑使用模板方法模式。
六、结语
欢迎批评指正!
结束!