C++ 模板方法模式(Template Method Pattern)
一、模式基础概述
1.1 定义
模板方法模式属于行为型设计模式,在抽象父类中定义一套完整算法流程骨架,拆分出固定执行步骤与可变业务步骤;将差异化步骤交由子类重写实现,整体算法执行顺序不可修改,实现流程统一、细节定制化开发。
1.2 核心思想
流程骨架固定不变,内部执行步骤按需扩展;父类管控执行时序,子类仅负责具体业务实现,遵循好莱坞原则,父类主动调度子类方法。
1.3 设计原则
- 开闭原则
:新增业务场景只需扩展子类,无需修改原有父类流程代码 - 单一职责
:父类专注流程编排,子类专注步骤逻辑实现 - 代码复用
:公共通用步骤统一封装在父类,减少重复编码
二、核心组成角色
三、模式运行流程
客户端创建具体子类实例,向上转型为抽象父类 调用父类模板方法,启动整套算法流程 模板方法按照既定顺序,依次调用固定步骤、抽象步骤、钩子判断 固定步骤执行父类通用逻辑,抽象步骤自动匹配子类重写逻辑 钩子方法根据子类实现结果,判定是否执行分支步骤
四、基础经典示例:饮品制作流程
#include<iostream>#include<string>// 抽象饮品父类class Beverage{public:// 模板方法:固定制作流程,禁止重写voidmakeDrink()final{boilWater();brewing();pourCup();if (needCondiment()){addCondiment();}}virtual ~Beverage() = default;protected:// 通用固定步骤voidboilWater(){std::cout << "步骤1:烧开水\n";}voidpourCup(){std::cout << "步骤3:倒入杯中\n";}// 抽象差异化步骤virtualvoidbrewing()= 0;virtualvoidaddCondiment()= 0;// 钩子方法:可选添加配料virtualboolneedCondiment(){return true;}};// 具体子类:泡茶class Tea : public Beverage{protected:voidbrewing()override{std::cout << "步骤2:浸泡茶叶\n";}voidaddCondiment()override{std::cout << "步骤4:加入柠檬调味\n";}};// 具体子类:煮咖啡class Coffee : public Beverage{protected:voidbrewing()override{std::cout << "步骤2:冲泡咖啡粉\n";}voidaddCondiment()override{std::cout << "步骤4:加入牛奶白糖\n";}// 重写钩子,自定义配料选择boolneedCondiment()override{std::string choice;std::cout << "是否添加配料(y/n):";std::cin >> choice;return choice == "y" || choice == "Y";}};// 客户端调用intmain(){Beverage* tea = new Tea();Beverage* coffee = new Coffee();std::cout << "===== 开始泡茶 =====\n";tea->makeDrink();std::cout << "\n===== 开始煮咖啡 =====\n";coffee->makeDrink();delete tea;delete coffee;return 0;}
五、工程实战示例:通用数据处理器
#include<iostream>#include<memory>// 抽象数据处理父类class DataHandle{public:// 固定数据处理模板流程voidhandleData()final{loadSource();parseContent();dataCheck();formatConvert();storeResult();printLog();}virtual ~DataHandle() = default;protected:// 全局通用步骤voidloadSource(){std::cout << "1. 读取原始数据源\n";}voiddataCheck(){std::cout << "3. 合法性数据校验\n";}voidstoreResult(){std::cout << "5. 持久化存储处理结果\n";}voidprintLog(){std::cout << "6. 输出处理日志\n";}// 子类差异化实现步骤virtualvoidparseContent()= 0;virtualvoidformatConvert()= 0;};// CSV格式数据处理class CsvHandle : public DataHandle{protected:voidparseContent()override{std::cout << "2. 解析CSV格式数据\n";}voidformatConvert()override{std::cout << "4. 转换CSV业务格式\n";}};// JSON格式数据处理class JsonHandle : public DataHandle{protected:voidparseContent()override{std::cout << "2. 解析JSON格式数据\n";}voidformatConvert()override{std::cout << "4. 转换JSON业务格式\n";}};intmain(){std::unique_ptr<DataHandle> csvTask = std::make_unique<CsvHandle>();std::unique_ptr<DataHandle> jsonTask = std::make_unique<JsonHandle>();std::cout << "=== 处理CSV数据 ===\n";csvTask->handleData();std::cout << "\n=== 处理JSON数据 ===\n";jsonTask->handleData();return 0;}
六、钩子方法详细说明
- 作用
:作为流程扩展开关,不改动主流程骨架,灵活控制分支执行 - 写法规范
:父类提供默认实现,子类按需选择性重写 - 常见场景
:条件判断、功能启停、参数自定义、前置后置回调
七、模式优缺点
7.1 优点
公共逻辑集中封装,大幅降低代码冗余,复用性极强 执行流程统一管控,避免子类随意篡改业务顺序 扩展便捷,新增业务仅编写子类即可,符合开闭原则 层级结构清晰,流程与业务解耦,后期维护难度低 适配框架生命周期开发,统一规范业务执行逻辑
7.2 缺点
每一种差异化业务都需要新建子类,类文件数量增多 反向调用逻辑理解门槛较高,新手不易梳理执行链路 父类流程骨架一旦修改,所有子类都会受到影响 继承耦合度较高,子类逻辑依赖父类结构
八、适用业务场景
多个业务模块具备相同执行流程,仅内部细节存在差异 需要严格约束步骤执行顺序,防止流程错乱 框架、SDK设计,定义组件生命周期、扩展预留接口 文件解析、数据转换、报表生成、批量任务流水线 游戏AI行为、定时任务、统一接口业务处理
九、相近设计模式对比
十、C++编码最佳实践
核心模板方法必须使用 final修饰,禁止子类重写破坏流程内部步骤统一设置为 protected权限,隔绝外部非法调用必须实现步骤定义为纯虚函数,可选步骤设计为钩子虚函数 父类只负责流程调度,不嵌入具体业务实现代码 采用智能指针管理多态对象,避免内存泄漏 钩子方法逻辑轻量化,不侵占主业务执行流程
十一、模式总结
模板方法模式核心精髓为骨架固定,步骤可变,依托继承体系拆分通用逻辑与个性逻辑。统一规范业务执行流程,最大化复用公共代码,通过子类完成差异化定制。该模式广泛应用于C++框架开发、数据处理、流水线任务等场景,是把控业务流程、规范代码结构的常用基础行为模式。
夜雨聆风