行为型模式:⑩模板模式(Template Pattern)

行为型模式:⑩模板模式(Template Pattern)

核心思想

定义一个算法的骨架(模板方法),将算法中可变的步骤延迟到子类/具体实现中。核心价值是:

  • 复用代码:固定算法的整体流程(如“煮水→冲泡→装杯→加调料”),避免重复编写相同逻辑;
  • 约束流程:确保算法步骤的执行顺序不可修改,子类仅能修改局部可变步骤;
  • 开放扩展:新增具体实现时,无需修改模板骨架,仅需实现可变步骤,符合“开闭原则”。

核心本质

流程固化+细节延迟,将“不变的流程”与“可变的细节”分离,既保证一致性,又提供灵活性。

典型场景

  • 框架级流程模板(如Spring的Bean生命周期、测试框架的“setup→run→teardown”);
  • 业务固定流程(如订单支付流程“验证→扣款→通知→记账”,仅扣款方式可变);
  • 同类产品的统一流程(如制作饮料、快餐套餐、文档导出)。

C语言实现(结构体+函数指针+模板方法)

C语言无类和继承,通过“结构体模拟模板骨架”“函数指针定义可变步骤”“模板方法封装固定流程”,实现流程复用与细节延迟。核心是:模板结构体持有可变步骤的函数指针,模板方法按顺序调用这些指针,具体实现通过填充函数指针完成。

场景示例

制作饮料:

  • 固定流程(模板方法):煮水 → 冲泡 → 倒入杯子 → 加调料;
  • 可变步骤:冲泡(咖啡粉/茶叶)、加调料(牛奶糖/柠檬);
  • 抽象模板:BeverageTemplate结构体(包含模板方法和可变步骤函数指针);
  • 具体模板:咖啡(Coffee)、茶(Tea),实现各自的“冲泡”和“加调料”。

实现代码

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// -------------------------- 1. 抽象模板(定义算法骨架和可变步骤接口) --------------------------
// 前置声明:抽象模板结构体(供函数指针使用)
typedef struct BeverageTemplate BeverageTemplate;

// 可变步骤的函数指针类型(相当于抽象方法)
typedef void (*BrewFunc)(const BeverageTemplate* self);    // 冲泡
typedef void (*AddCondimentsFunc)(const BeverageTemplate* self); // 加调料

// 抽象模板结构体:包含模板方法和可变步骤函数指针
typedef struct BeverageTemplate {
    char name[32];                // 饮料名称(用于日志)
    BrewFunc brew;                // 可变步骤1:冲泡
    AddCondimentsFunc add_condiments; // 可变步骤2:加调料
    // 模板方法(核心:固定算法骨架,不可修改)
    void (*make)(struct BeverageTemplate* self);
    // 销毁
    void (*destroy)(struct BeverageTemplate* self);
} BeverageTemplate;

// 模板方法:固定流程(煮水→冲泡→装杯→加调料)
void beverage_make(BeverageTemplate* self) {
    if (self == NULL || self->brew == NULL || self->add_condiments == NULL) {
        printf("错误:模板初始化不完整!\n");
        return;
    }
    printf("=== 开始制作「%s」 ===\n", self->name);
    // 固定步骤1:煮水(所有饮料通用)
    printf("1. 煮水(100℃)...\n");
    // 可变步骤1:冲泡(由具体模板实现)
    self->brew(self);
    // 固定步骤2:倒入杯子(所有饮料通用)
    printf("3. 倒入杯子...\n");
    // 可变步骤2:加调料(由具体模板实现)
    self->add_condiments(self);
    printf("=== 「%s」制作完成! ===\n\n", self->name);
}

// 抽象模板销毁(基类销毁逻辑,具体模板可扩展)
void beverage_destroy(BeverageTemplate* self) {
    free(self);
}

// -------------------------- 2. 具体模板1:咖啡(实现可变步骤) --------------------------
// 咖啡的“冲泡”实现
void coffee_brew(const BeverageTemplate* self) {
    printf("2. 冲泡咖啡粉(使用沸水萃取)...\n");
}

// 咖啡的“加调料”实现
void coffee_add_condiments(const BeverageTemplate* self) {
    printf("4. 加入牛奶和方糖...\n");
}

// 创建咖啡(具体模板实例化)
BeverageTemplate* create_coffee() {
    BeverageTemplate* coffee = (BeverageTemplate*)malloc(sizeof(BeverageTemplate));
    strcpy(coffee->name, "美式咖啡");
    // 绑定具体实现到函数指针(实现可变步骤)
    coffee->brew = coffee_brew;
    coffee->add_condiments = coffee_add_condiments;
    // 绑定模板方法(固定流程)
    coffee->make = beverage_make;
    coffee->destroy = beverage_destroy;
    return coffee;
}

// -------------------------- 3. 具体模板2:茶(实现可变步骤) --------------------------
// 茶的“冲泡”实现
void tea_brew(const BeverageTemplate* self) {
    printf("2. 冲泡茶叶(85℃水温浸泡)...\n");
}

// 茶的“加调料”实现
void tea_add_condiments(const BeverageTemplate* self) {
    printf("4. 加入柠檬片...\n");
}

// 创建茶(具体模板实例化)
BeverageTemplate* create_tea() {
    BeverageTemplate* tea = (BeverageTemplate*)malloc(sizeof(BeverageTemplate));
    strcpy(tea->name, "柠檬红茶");
    // 绑定具体实现到函数指针
    tea->brew = tea_brew;
    tea->add_condiments = tea_add_condiments;
    // 复用模板方法(固定流程)
    tea->make = beverage_make;
    tea->destroy = beverage_destroy;
    return tea;
}

// -------------------------- 测试代码(客户端) --------------------------
int main() {
    // 1. 创建具体模板实例
    BeverageTemplate* coffee = create_coffee();
    BeverageTemplate* tea = create_tea();

    // 2. 调用模板方法(触发固定流程,自动执行具体实现)
    coffee->make(coffee);
    tea->make(tea);

    // 3. 销毁资源
    coffee->destroy(coffee);
    tea->destroy(tea);

    return 0;
}

C语言实现关键要点

  1. 模板方法封装固定流程beverage_make是核心,按“煮水→冲泡→装杯→加调料”的固定顺序执行,其中通用步骤(煮水、装杯)直接实现,可变步骤(冲泡、加调料)通过函数指针延迟到具体模板。
  2. 函数指针模拟抽象方法BrewFuncAddCondimentsFunc是可变步骤的接口,具体模板(咖啡、茶)通过实现对应函数并绑定到指针,完成“延迟实现”。
  3. 复用与扩展分离:模板方法(固定流程)在抽象模板中统一实现,具体模板仅需关注自身的可变步骤,无需重复编写通用逻辑,复用性强。
  4. 结构一致性:所有具体模板都通过BeverageTemplate结构体统一接口,客户端调用make方法即可触发流程,无需区分具体类型,符合“依赖倒置原则”。
  5. 内存管理:抽象模板提供destroy方法,具体模板可复用或扩展,避免内存泄漏;函数指针绑定需确保非空,避免野指针调用。
  6. 灵活性:新增饮料(如奶茶)时,仅需实现brewadd_condiments函数,创建BeverageTemplate实例并绑定指针,无需修改模板方法,符合“开闭原则”。

C++实现(抽象基类+继承+纯虚函数)

C++通过“抽象基类定义模板方法和纯虚函数”“子类继承并实现纯虚函数”,天然适配模板模式。核心是:模板方法为非虚函数(固定流程不可重写),可变步骤为纯虚函数(强制子类实现),充分利用面向对象特性实现复用与扩展。

场景示例

同C语言:制作饮料,抽象基类Beverage定义模板方法makeBeverage(固定流程),纯虚函数brewaddCondiments(可变步骤),子类CoffeeTea实现纯虚函数。

实现代码

#include <iostream>
#include <string>

using namespace std;

// -------------------------- 1. 抽象模板(抽象基类) --------------------------
class Beverage {
public:
    virtual ~Beverage() = default; // 虚析构:确保子类析构被调用

    // 模板方法(核心:固定算法骨架,非虚函数→不可重写,保证流程固定)
    void makeBeverage() {
        cout << "=== 开始制作「" << getName() << "」 ===" << endl;
        boilWater();    // 固定步骤1:煮水(通用)
        brew();         // 可变步骤1:冲泡(纯虚函数,子类实现)
        pourInCup();    // 固定步骤2:装杯(通用)
        addCondiments();// 可变步骤2:加调料(纯虚函数,子类实现)
        cout << "=== 「" << getName() << "」制作完成! ===\n" << endl;
    }

    // 获取饮料名称(纯虚函数:子类必须指定名称)
    virtual string getName() const = 0;

protected:
    // 固定步骤:所有饮料通用,子类不可修改(可设为private,若无需子类访问)
    void boilWater() const {
        cout << "1. 煮水(100℃)..." << endl;
    }

    void pourInCup() const {
        cout << "3. 倒入杯子..." << endl;
    }

    // 可变步骤:纯虚函数(强制子类实现,延迟到具体模板)
    virtual void brew() const = 0;         // 冲泡
    virtual void addCondiments() const = 0;// 加调料
};

// -------------------------- 2. 具体模板1:咖啡(子类实现可变步骤) --------------------------
class Coffee : public Beverage {
public:
    string getName() const override {
        return "美式咖啡";
    }

protected:
    void brew() const override {
        cout << "2. 冲泡咖啡粉(使用沸水萃取)..." << endl;
    }

    void addCondiments() const override {
        cout << "4. 加入牛奶和方糖..." << endl;
    }
};

// -------------------------- 3. 具体模板2:茶(子类实现可变步骤) --------------------------
class Tea : public Beverage {
public:
    string getName() const override {
        return "柠檬红茶";
    }

protected:
    void brew() const override {
        cout << "2. 冲泡茶叶(85℃水温浸泡)..." << endl;
    }

    void addCondiments() const override {
        cout << "4. 加入柠檬片..." << endl;
    }
};

// -------------------------- 测试代码(客户端) --------------------------
int main() {
    // 1. 创建具体模板实例(父类指针指向子类对象,多态特性)
    Beverage* coffee = new Coffee();
    Beverage* tea = new Tea();

    // 2. 调用模板方法(固定流程,自动执行子类实现)
    coffee->makeBeverage();
    tea->makeBeverage();

    // 3. 销毁资源(虚析构确保子类析构被调用)
    delete coffee;
    delete tea;

    return 0;
}

C++实现关键要点

  1. 模板方法为非虚函数makeBeverage是核心,设为非虚函数确保子类无法重写,保证算法流程固定(如“煮水→冲泡”的顺序不可修改)。
  2. 纯虚函数强制实现brewaddCondimentsgetName为纯虚函数,强制子类实现,避免“未实现可变步骤”的错误,符合“接口隔离原则”。
  3. protected访问控制:通用步骤(boilWaterpourInCup)设为protected,子类可访问但不可修改;可变步骤设为protected纯虚函数,仅子类可见,封装性更强。
  4. 继承与多态:子类继承抽象基类,实现纯虚函数,客户端通过基类指针调用模板方法,无需关心具体子类类型,符合“依赖倒置原则”。
  5. 扩展性极强:新增饮料(如奶茶)时,仅需创建子类继承Beverage,实现纯虚函数,无需修改模板方法和其他子类,完全符合“开闭原则”。
  6. 虚析构函数:抽象基类析构函数设为虚函数,确保删除基类指针时,子类析构函数被正确调用,避免内存泄漏。

模板模式核心总结(C vs C++)

对比维度C语言实现C++语言实现
接口定义结构体+函数指针(模拟抽象方法)抽象基类+纯虚函数(强制子类实现)
模板方法特性普通函数(通过结构体绑定,流程固定)非虚函数(不可重写,流程强制固定)
可变步骤约束依赖编程规范(函数指针需绑定)编译期强制(纯虚函数必须实现)
继承模拟结构体复用+函数指针绑定类继承+多态(天然支持)
封装性结构体成员直接暴露,封装性一般protected/private访问控制,封装性强
扩展性新增具体模板需绑定函数指针新增子类仅需实现纯虚函数
类型安全依赖手动指针操作,风险高编译期类型检查+多态,类型安全
代码简洁度需手动维护结构体和函数指针,繁琐继承+纯虚函数,代码优雅简洁

通用关键要点(跨语言)

设计原则

  • 开闭原则:新增具体模板无需修改抽象模板和其他模板,仅扩展实现;
  • 里氏替换原则:具体模板可替代抽象模板,客户端通过抽象模板接口调用;
  • 单一职责原则:抽象模板管流程,具体模板管细节,各司其职。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值