一、什么是模板方法模式?

模板方法模式的核心思想是:在父类中定义一个算法的骨架(模板方法),将算法中可变的步骤延迟到子类中实现。

可以用生活中的例子理解:

  • 泡一杯饮料的通用流程(骨架):烧开水 → 冲泡原料 → 倒入杯子 → 加辅料。其中 “烧开水、倒入杯子” 是固定步骤,“冲泡原料(茶 / 咖啡)、加辅料(糖 / 奶)” 是可变步骤。
  • 父类(饮料类)定义整个流程的模板方法,子类(茶类、咖啡类)只需要实现自己的可变步骤即可。

模板方法模式的核心角色:

  1. 抽象类(Abstract Class):
    • 定义模板方法:包含算法的骨架,按固定顺序调用各个步骤。
    • 定义基本方法:分为两类:
      • 固定方法:父类实现,子类不能修改(如烧开水)。
      • 抽象方法:子类必须实现(如冲泡原料)。
      • 钩子方法(可选):父类提供默认实现,子类可选择重写(如加辅料)。
  2. 具体子类(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. (默认)不加任何辅料

三、关键细节说明

  1. 模板方法的不可变性:示例中 prepareRecipe() 声明为 final(C++11 特性),目的是禁止子类修改算法的整体流程,确保固定步骤的执行顺序不被破坏。如果你的编译器不支持 C++11,也可以将其声明为普通 virtual,但需约定子类不重写该方法。

  2. 访问控制:固定方法、抽象方法、钩子方法都声明为 protected,原因是:

    • 这些方法是算法的内部步骤,不应该被外部直接调用(只通过模板方法触发)。
    • 子类可以访问并实现 / 重写这些方法。
  3. 钩子方法的作用:钩子方法是 “可选扩展点”,比如示例中茶类可以选择是否重写 addCondiments(),既保留了默认行为,又给子类灵活扩展的空间。

四、实际应用场景

  1. 框架设计:比如 Qt 中的 QWidget 类,paintEvent() 是抽象方法,show() 是模板方法(包含初始化、绘制、显示等固定步骤,绘制步骤交给子类实现)。
  2. 算法标准化:比如排序算法框架,父类定义 “初始化 → 排序 → 结果校验” 的骨架,子类实现不同的排序逻辑(冒泡、快排、归并)。
  3. 业务流程封装:比如订单处理流程(创建订单 → 校验库存 → 支付 → 发货),其中 “校验库存、支付” 是可变步骤,交给不同业务子类实现。

总结

  1. 模板方法模式的核心是父类定骨架、子类填细节,将不变的流程固化,可变的逻辑延迟到子类实现。
  2. C++ 实现时,模板方法建议用 final 修饰,避免子类篡改流程;内部步骤用 protected 控制访问权限。
  3. 钩子方法是灵活扩展点,可提供默认实现,让子类按需重写,平衡 “标准化” 和 “灵活性”。
Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐