12.C++设计模式-模板方法模式

1. 概念

模板方法模式是一种行为设计模式,它在父类中定义一个算法的骨架,允许子类在不改变算法结构的情况下重定义算法的某些步骤。

2. 核心组成

«abstract»

AbstractClass

+TemplateMethod()

#PrimitiveOperation1()

#PrimitiveOperation2()

+HookMethod()

ConcreteClassA

#PrimitiveOperation1()

#PrimitiveOperation2()

ConcreteClassB

#PrimitiveOperation1()

#PrimitiveOperation2()

3. 角色说明

  • 抽象类(AbstractClass):定义模板方法和基本操作
  • 具体类(ConcreteClass):实现基本操作
  • 模板方法(TemplateMethod):定义算法骨架
  • 基本操作(PrimitiveOperation):子类必须实现的抽象方法
  • 钩子方法(HookMethod):可选扩展点

4. 应用场景

  • 数据库操作模板(连接、查询、关闭)
  • 游戏开发(游戏初始化、开始、结束)
  • 饮料制作(煮水、冲泡、倒杯、加调料)
  • 数据挖掘(数据读取、分析、输出)
  • 算法框架(排序、搜索算法的固定流程)

5. C++代码示例

示例1:饮料制作
#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:数据处理器
#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. 最佳实践建议

  1. 合理使用钩子方法:提供适当的扩展点
  2. 明确抽象粒度:抽象方法不要过多过细
  3. 遵守好莱坞原则:Don’t call us, we’ll call you
  4. 组合优于继承:考虑是否真的需要使用模板方法模式

8.模板方法模式的思想思维深度解析

8.1 核心思想:控制反转与好莱坞原则

模板方法模式最核心的思想是 “Don’t call us, we’ll call you”(别打电话给我们,我们会打给你)。

模板方法思维

规定流程

反向调用

反向调用

规定流程

父类模板方法

步骤1

子类实现步骤2

子类实现步骤3

步骤4

传统思维

主动调用

主动调用

主动调用

主程序

子程序A

子程序B

子程序C

思维转变:从"我来调用工具"转变为"框架来调度我"。

8.2 抽象思维:骨架与细节的分离

稳定vs变化

包含

包含

包含

包含

包含

包含

稳定的算法骨架
不易变化

步骤顺序

流程结构

公共行为

变化的具体步骤
容易变化

实现细节

算法变体

业务差异

思维要点

  • 识别不变:什么流程是永远不变的?
  • 封装变化:什么细节是可能改变的?
  • 分离关注:框架作者关心"骨架",应用开发者关心"细节"
8.3 哲学思维:契约式设计
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 工程思维:稳定性与扩展性的平衡
// 体现稳定性的代码
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 思维升华:从设计模式到架构思维
// 这就是微服务的模板方法思维
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 核心思维总结
思维维度核心要点境界
控制思维反转控制,框架主导信任框架
抽象思维分离不变与变化识别边界
契约思维明确接口约定建立规范
系统思维考虑整体架构全局视野
工程思维权衡稳定与扩展实用主义

最后的思考

模板方法模式本质上是一种元思维——你不仅在解决当前问题,更是在思考"如何组织未来可能变化的解决方案"。它教会我们:

  1. 预见变化:在设计时就考虑哪些部分可能会变化
  2. 建立框架:先构建稳定的骨架,再填充细节
  3. 反转视角:从"被调用者"转变为"调用者"
  4. 契约精神:明确谁负责什么,建立清晰的边界

这不仅仅是一个编码技巧,更是一种架构思维——好的架构师会设计出既稳定又灵活的"框架",让团队成员在这个框架下各司其职,共同构建复杂的系统。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值