1. 概念
模板方法模式是一种行为设计模式,它在父类中定义一个算法的骨架,允许子类在不改变算法结构的情况下重定义算法的某些步骤。
2. 核心组成
<<abstract>>
AbstractClass
+TemplateMethod()
#PrimitiveOperation1()
#PrimitiveOperation2()
+HookMethod()
ConcreteClassA
#PrimitiveOperation1()
#PrimitiveOperation2()
ConcreteClassB
#PrimitiveOperation1()
#PrimitiveOperation2()
3. 角色说明
- 抽象类(AbstractClass):定义模板方法和基本操作
- 具体类(ConcreteClass):实现基本操作
- 模板方法(TemplateMethod):定义算法骨架
- 基本操作(PrimitiveOperation):子类必须实现的抽象方法
- 钩子方法(HookMethod):可选扩展点
4. 应用场景
- 数据库操作模板(连接、查询、关闭)
- 游戏开发(游戏初始化、开始、结束)
- 饮料制作(煮水、冲泡、倒杯、加调料)
- 数据挖掘(数据读取、分析、输出)
- 算法框架(排序、搜索算法的固定流程)
5. C++代码示例
示例1:饮料制作
cpp
#include <iostream>
using namespace std;
// 抽象类:饮料
class Beverage {
public:
// 模板方法 - 制作饮料的完整流程
void MakeBeverage() {
BoilWater();
Brew();
PourInCup();
AddCondiments();
// 钩子方法示例
if (WantSugar()) {
AddSugar();
}
}
protected:
void BoilWater() {
cout << "把水煮沸" << endl;
}
void PourInCup() {
cout << "倒入杯子中" << endl;
}
// 抽象方法,子类必须实现
virtual void Brew() = 0;
virtual void AddCondiments() = 0;
// 钩子方法,子类可选择覆盖
virtual bool WantSugar() {
return false; // 默认不加糖
}
void AddSugar() {
cout << "加糖" << endl;
}
};
// 具体类:咖啡
class Coffee : public Beverage {
protected:
void Brew() override {
cout << "冲泡咖啡粉" << endl;
}
void AddCondiments() override {
cout << "加牛奶和糖" << endl;
}
bool WantSugar() override {
char choice;
cout << "咖啡是否需要加糖?(y/n): ";
cin >> choice;
return (choice == 'y' || choice == 'Y');
}
};
// 具体类:茶
class Tea : public Beverage {
protected:
void Brew() override {
cout << "浸泡茶叶" << endl;
}
void AddCondiments() override {
cout << "加柠檬" << endl;
}
};
// 使用示例
int main() {
cout << "=== 制作咖啡 ===" << endl;
Coffee coffee;
coffee.MakeBeverage();
cout << "\n=== 制作茶 ===" << endl;
Tea tea;
tea.MakeBeverage();
return 0;
}
示例2:数据处理器
cpp
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
// 抽象数据处理器
class DataProcessor {
public:
// 模板方法
void ProcessData() {
if (ValidateData()) {
LoadData();
AnalyzeData();
OutputResult();
} else {
cout << "数据验证失败!" << endl;
}
}
protected:
vector<int> data;
// 钩子方法:数据验证
virtual bool ValidateData() {
return !data.empty();
}
// 抽象方法
virtual void LoadData() = 0;
virtual void AnalyzeData() = 0;
virtual void OutputResult() = 0;
};
// 排序处理器
class SortProcessor : public DataProcessor {
protected:
void LoadData() override {
data = {5, 2, 8, 1, 9, 3};
cout << "加载数据: ";
PrintData();
}
void AnalyzeData() override {
sort(data.begin(), data.end());
cout << "执行排序分析" << endl;
}
void OutputResult() override {
cout << "排序结果: ";
PrintData();
}
private:
void PrintData() {
for (int num : data) {
cout << num << " ";
}
cout << endl;
}
};
// 统计处理器
class StatisticsProcessor : public DataProcessor {
protected:
void LoadData() override {
data = {10, 20, 30, 40, 50};
cout << "加载统计数据: ";
PrintData();
}
void AnalyzeData() override {
sum = 0;
for (int num : data) {
sum += num;
}
average = sum / data.size();
cout << "计算统计指标" << endl;
}
void OutputResult() override {
cout << "数据统计结果:" << endl;
cout << "总和: " << sum << endl;
cout << "平均值: " << average << endl;
cout << "数据个数: " << data.size() << endl;
}
private:
int sum;
int average;
void PrintData() {
for (int num : data) {
cout << num << " ";
}
cout << endl;
}
};
int main() {
cout << "=== 排序处理器 ===" << endl;
SortProcessor sortProc;
sortProc.ProcessData();
cout << "\n=== 统计处理器 ===" << endl;
StatisticsProcessor statProc;
statProc.ProcessData();
return 0;
}
6. 优缺点分析
缺点
维护成本
增加代码复杂度
继承耦合
子类受父类约束
理解难度
初学者理解困难
优点
代码复用
减少重复代码
扩展性
易于添加新实现
控制反转
父类控制算法
7. 最佳实践建议
- 合理使用钩子方法:提供适当的扩展点
- 明确抽象粒度:抽象方法不要过多过细
- 遵守好莱坞原则:Don't call us, we'll call you
- 组合优于继承:考虑是否真的需要使用模板方法模式
8.模板方法模式的思想思维深度解析
8.1 核心思想:控制反转与好莱坞原则
模板方法模式最核心的思想是 "Don't call us, we'll call you"(别打电话给我们,我们会打给你)。
模板方法思维
规定流程
反向调用
反向调用
规定流程
父类模板方法
步骤1
子类实现步骤2
子类实现步骤3
步骤4
传统思维
主动调用
主动调用
主动调用
主程序
子程序A
子程序B
子程序C
思维转变:从"我来调用工具"转变为"框架来调度我"。
8.2 抽象思维:骨架与细节的分离
稳定vs变化
包含
包含
包含
包含
包含
包含
稳定的算法骨架
不易变化
步骤顺序
流程结构
公共行为
变化的具体步骤
容易变化
实现细节
算法变体
业务差异
思维要点:
- 识别不变:什么流程是永远不变的?
- 封装变化:什么细节是可能改变的?
- 分离关注:框架作者关心"骨架",应用开发者关心"细节"
8.3 哲学思维:契约式设计
cpp
class GameFramework {
public:
// 这是框架与开发者之间的"契约"
// 框架保证:按照这个顺序调用
// 开发者保证:实现这些方法
void RunGame() {
Initialize(); // 步骤1
LoadAssets(); // 步骤2
while (isRunning) {
ProcessInput(); // 步骤3
Update(); // 步骤4
Render(); // 步骤5
}
Cleanup(); // 步骤6
}
protected:
virtual void Initialize() = 0; // 开发者必须实现
virtual void ProcessInput() = 0; // 开发者必须实现
virtual void Update() = 0; // 开发者必须实现
virtual void Render() = 0; // 开发者必须实现
// 钩子方法:可选的扩展点
virtual void LoadAssets() {}
virtual void Cleanup() {}
private:
bool isRunning = true;
};
思维意义:
- 框架作者:"我会在正确的时间调用你的代码"
- 应用开发者:"我只用关心具体业务逻辑"
8.4 结构化思维:模板方法的生命周期
模板方法调用
步骤检查:前置验证
步骤检查
步骤1执行
钩子判断1:可选步骤
钩子判断1
步骤2执行:如果钩子返回true
步骤3执行:如果钩子返回false
步骤2执行
步骤3执行
钩子判断2:后置处理
钩子判断2
步骤4执行:可选后置
步骤4执行
8.5 类比思维:现实世界的模板方法
餐厅用餐流程:
可变细节
不同餐厅
菜单不同
吃面条/米饭
用筷子/刀叉
现金/刷卡/扫码
固定流程
进店
点餐
用餐
结账
离开
软件开发类比:
- 框架 = 餐厅管理规范
- 模板方法 = 服务流程
- 钩子方法 = 特色服务(餐后送水果)
- 具体类 = 具体的餐厅(肯德基vs海底捞)
8.6 工程思维:稳定性与扩展性的平衡
cpp
// 体现稳定性的代码
class StableWorkflow {
public:
void Execute() final { // final禁止子类修改流程
Step1();
Step2();
if (Condition()) {
Step3();
}
Step4();
}
protected:
void Step1() { /* 稳定的实现 */ }
void Step2() { /* 稳定的实现 */ }
void Step4() { /* 稳定的实现 */ }
// 扩展点
virtual bool Condition() { return true; }
virtual void Step3() = 0; // 必须扩展的点
};
工程智慧:
- 8/2法则:80%的代码稳定,20%的代码变化
- 最小惊讶原则:流程应该符合直觉
- 开放-封闭原则:对扩展开放,对修改关闭
8.7 系统思维:模板方法在架构中的位置
设计模式关系
架构层次
调用
反向调用
限制
驱动
包含
可与组合
可与结合
应用层
模板方法
框架层
具体实现
插件层
系统约束
业务需求
工厂方法
策略模式
观察者模式
8.8 辩证思维:何时使用,何时避免
使用场景(绿色):
- ✅ 多个类有相同的算法骨架
- ✅ 需要控制子类扩展的点
- ✅ 需要重构重复代码到父类
- ✅ 框架设计中的标配
避免场景(红色):
- ❌ 只有一个具体实现(过度设计)
- ❌ 算法步骤变化频繁(策略模式更好)
- ❌ 子类需要覆盖大部分步骤(设计有问题)
- ❌ 继承深度过大(违反组合优先原则)
8.9 思维升华:从设计模式到架构思维
cpp
// 这就是微服务的模板方法思维
class MicroserviceFramework {
public:
void Run() { // 这就是"服务网格"的控制逻辑
RegisterService();
ConnectToConfigCenter();
SetupMetrics();
if (EnableTracing()) { // 钩子:是否启用链路追踪
SetupTracing();
}
StartServer();
HandleGracefulShutdown();
}
protected:
virtual void StartServer() = 0;
virtual void HandleGracefulShutdown() = 0;
virtual bool EnableTracing() { return false; }
virtual void SetupTracing() {}
};
8.10 核心思维总结
| 思维维度 | 核心要点 | 境界 |
|---|---|---|
| 控制思维 | 反转控制,框架主导 | 信任框架 |
| 抽象思维 | 分离不变与变化 | 识别边界 |
| 契约思维 | 明确接口约定 | 建立规范 |
| 系统思维 | 考虑整体架构 | 全局视野 |
| 工程思维 | 权衡稳定与扩展 | 实用主义 |
最后的思考:
模板方法模式本质上是一种元思维------你不仅在解决当前问题,更是在思考"如何组织未来可能变化的解决方案"。它教会我们:
- 预见变化:在设计时就考虑哪些部分可能会变化
- 建立框架:先构建稳定的骨架,再填充细节
- 反转视角:从"被调用者"转变为"调用者"
- 契约精神:明确谁负责什么,建立清晰的边界
这不仅仅是一个编码技巧,更是一种架构思维------好的架构师会设计出既稳定又灵活的"框架",让团队成员在这个框架下各司其职,共同构建复杂的系统。