行为型模式:⑩模板模式(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语言实现关键要点
- 模板方法封装固定流程:
beverage_make是核心,按“煮水→冲泡→装杯→加调料”的固定顺序执行,其中通用步骤(煮水、装杯)直接实现,可变步骤(冲泡、加调料)通过函数指针延迟到具体模板。 - 函数指针模拟抽象方法:
BrewFunc和AddCondimentsFunc是可变步骤的接口,具体模板(咖啡、茶)通过实现对应函数并绑定到指针,完成“延迟实现”。 - 复用与扩展分离:模板方法(固定流程)在抽象模板中统一实现,具体模板仅需关注自身的可变步骤,无需重复编写通用逻辑,复用性强。
- 结构一致性:所有具体模板都通过
BeverageTemplate结构体统一接口,客户端调用make方法即可触发流程,无需区分具体类型,符合“依赖倒置原则”。 - 内存管理:抽象模板提供
destroy方法,具体模板可复用或扩展,避免内存泄漏;函数指针绑定需确保非空,避免野指针调用。 - 灵活性:新增饮料(如奶茶)时,仅需实现
brew和add_condiments函数,创建BeverageTemplate实例并绑定指针,无需修改模板方法,符合“开闭原则”。
C++实现(抽象基类+继承+纯虚函数)
C++通过“抽象基类定义模板方法和纯虚函数”“子类继承并实现纯虚函数”,天然适配模板模式。核心是:模板方法为非虚函数(固定流程不可重写),可变步骤为纯虚函数(强制子类实现),充分利用面向对象特性实现复用与扩展。
场景示例
同C语言:制作饮料,抽象基类Beverage定义模板方法makeBeverage(固定流程),纯虚函数brew和addCondiments(可变步骤),子类Coffee和Tea实现纯虚函数。
实现代码
#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++实现关键要点
- 模板方法为非虚函数:
makeBeverage是核心,设为非虚函数确保子类无法重写,保证算法流程固定(如“煮水→冲泡”的顺序不可修改)。 - 纯虚函数强制实现:
brew、addCondiments、getName为纯虚函数,强制子类实现,避免“未实现可变步骤”的错误,符合“接口隔离原则”。 - protected访问控制:通用步骤(
boilWater、pourInCup)设为protected,子类可访问但不可修改;可变步骤设为protected纯虚函数,仅子类可见,封装性更强。 - 继承与多态:子类继承抽象基类,实现纯虚函数,客户端通过基类指针调用模板方法,无需关心具体子类类型,符合“依赖倒置原则”。
- 扩展性极强:新增饮料(如奶茶)时,仅需创建子类继承
Beverage,实现纯虚函数,无需修改模板方法和其他子类,完全符合“开闭原则”。 - 虚析构函数:抽象基类析构函数设为虚函数,确保删除基类指针时,子类析构函数被正确调用,避免内存泄漏。
模板模式核心总结(C vs C++)
| 对比维度 | C语言实现 | C++语言实现 |
|---|---|---|
| 接口定义 | 结构体+函数指针(模拟抽象方法) | 抽象基类+纯虚函数(强制子类实现) |
| 模板方法特性 | 普通函数(通过结构体绑定,流程固定) | 非虚函数(不可重写,流程强制固定) |
| 可变步骤约束 | 依赖编程规范(函数指针需绑定) | 编译期强制(纯虚函数必须实现) |
| 继承模拟 | 结构体复用+函数指针绑定 | 类继承+多态(天然支持) |
| 封装性 | 结构体成员直接暴露,封装性一般 | protected/private访问控制,封装性强 |
| 扩展性 | 新增具体模板需绑定函数指针 | 新增子类仅需实现纯虚函数 |
| 类型安全 | 依赖手动指针操作,风险高 | 编译期类型检查+多态,类型安全 |
| 代码简洁度 | 需手动维护结构体和函数指针,繁琐 | 继承+纯虚函数,代码优雅简洁 |
通用关键要点(跨语言)
设计原则:
- 开闭原则:新增具体模板无需修改抽象模板和其他模板,仅扩展实现;
- 里氏替换原则:具体模板可替代抽象模板,客户端通过抽象模板接口调用;
- 单一职责原则:抽象模板管流程,具体模板管细节,各司其职。
361

被折叠的 条评论
为什么被折叠?



