设计模式-模板方法
·
一、什么是模板方法模式?
模板方法模式的核心思想是:在父类中定义一个算法的骨架(模板方法),将算法中可变的步骤延迟到子类中实现。
可以用生活中的例子理解:
- 泡一杯饮料的通用流程(骨架):烧开水 → 冲泡原料 → 倒入杯子 → 加辅料。其中 “烧开水、倒入杯子” 是固定步骤,“冲泡原料(茶 / 咖啡)、加辅料(糖 / 奶)” 是可变步骤。
- 父类(饮料类)定义整个流程的模板方法,子类(茶类、咖啡类)只需要实现自己的可变步骤即可。
模板方法模式的核心角色:
- 抽象类(Abstract Class):
- 定义模板方法:包含算法的骨架,按固定顺序调用各个步骤。
- 定义基本方法:分为两类:
- 固定方法:父类实现,子类不能修改(如烧开水)。
- 抽象方法:子类必须实现(如冲泡原料)。
- 钩子方法(可选):父类提供默认实现,子类可选择重写(如加辅料)。
- 具体子类(Concrete Class):实现抽象类中的抽象方法,完成算法的可变步骤。
二、C++ 完整实现示例
以 “泡饮料” 为例,实现模板方法模式:
#include <iostream>
#include <string>
using namespace std;
// 抽象类:饮料类(定义算法骨架)
class Beverage {
public:
// 模板方法:定义泡饮料的完整流程(声明为 final 防止子类重写,C++11 及以上支持)
virtual void prepareRecipe() final {
boilWater(); // 固定步骤1:烧开水
brew(); // 可变步骤1:冲泡原料(子类实现)
pourInCup(); // 固定步骤2:倒入杯子
addCondiments(); // 可变步骤2:加辅料(子类实现/钩子)
}
protected:
// 固定方法:父类实现,子类无需修改(protected 避免外部调用)
void boilWater() {
cout << "1. 烧开水(100℃)" << endl;
}
void pourInCup() {
cout << "3. 将饮料倒入杯子" << endl;
}
// 抽象方法:子类必须实现
virtual void brew() = 0;
// 钩子方法:父类提供默认实现,子类可重写
virtual void addCondiments() {
cout << "4. (默认)不加任何辅料" << endl;
}
};
// 具体子类1:咖啡类
class Coffee : public Beverage {
protected:
// 实现抽象方法:冲泡咖啡粉
void brew() override {
cout << "2. 冲泡咖啡粉" << endl;
}
// 重写钩子方法:加糖和奶
void addCondiments() override {
cout << "4. 加入糖和牛奶" << endl;
}
};
// 具体子类2:茶类
class Tea : public Beverage {
protected:
// 实现抽象方法:冲泡茶叶
void brew() override {
cout << "2. 冲泡茶叶" << endl;
}
// 复用父类钩子方法(不加辅料),也可重写为加柠檬
// void addCondiments() override {
// cout << "4. 加入柠檬" << endl;
// }
};
// 测试代码
int main() {
cout << "===== 制作咖啡 =====" << endl;
Beverage* coffee = new Coffee();
coffee->prepareRecipe();
cout << "\n===== 制作茶 =====" << endl;
Beverage* tea = new Tea();
tea->prepareRecipe();
// 释放内存
delete coffee;
delete tea;
return 0;
}
输出结果:
plaintext
===== 制作咖啡 =====
1. 烧开水(100℃)
2. 冲泡咖啡粉
3. 将饮料倒入杯子
4. 加入糖和牛奶
===== 制作茶 =====
1. 烧开水(100℃)
2. 冲泡茶叶
3. 将饮料倒入杯子
4. (默认)不加任何辅料
三、关键细节说明
-
模板方法的不可变性:示例中
prepareRecipe()声明为final(C++11 特性),目的是禁止子类修改算法的整体流程,确保固定步骤的执行顺序不被破坏。如果你的编译器不支持 C++11,也可以将其声明为普通virtual,但需约定子类不重写该方法。 -
访问控制:固定方法、抽象方法、钩子方法都声明为
protected,原因是:- 这些方法是算法的内部步骤,不应该被外部直接调用(只通过模板方法触发)。
- 子类可以访问并实现 / 重写这些方法。
-
钩子方法的作用:钩子方法是 “可选扩展点”,比如示例中茶类可以选择是否重写
addCondiments(),既保留了默认行为,又给子类灵活扩展的空间。
四、实际应用场景
- 框架设计:比如 Qt 中的
QWidget类,paintEvent()是抽象方法,show()是模板方法(包含初始化、绘制、显示等固定步骤,绘制步骤交给子类实现)。 - 算法标准化:比如排序算法框架,父类定义 “初始化 → 排序 → 结果校验” 的骨架,子类实现不同的排序逻辑(冒泡、快排、归并)。
- 业务流程封装:比如订单处理流程(创建订单 → 校验库存 → 支付 → 发货),其中 “校验库存、支付” 是可变步骤,交给不同业务子类实现。
总结
- 模板方法模式的核心是父类定骨架、子类填细节,将不变的流程固化,可变的逻辑延迟到子类实现。
- C++ 实现时,模板方法建议用
final修饰,避免子类篡改流程;内部步骤用protected控制访问权限。 - 钩子方法是灵活扩展点,可提供默认实现,让子类按需重写,平衡 “标准化” 和 “灵活性”。
更多推荐

所有评论(0)