引言
模板方法模式(Template Method Pattern)是一种行为型设计模式。它定义算法骨架,将具体步骤延迟到子类实现。适用于固定流程但部分步骤可变的情景,如游戏初始化或数据处理。
定义
- 抽象类:定义模板方法(final方法)和抽象步骤。
- 具体子类:实现抽象步骤。
优点:代码复用,易扩展。缺点:子类过多时复杂。
classDiagram class Beverage { +prepare(): void +boilWater(): void +pourInCup(): void +brew(): void abstract +addCondiments(): void abstract } class Coffee { +brew(): void +addCondiments(): void } class Tea { +brew(): void +addCondiments(): void } Beverage <|-- Coffee Beverage <|-- Tea
TypeScript 示例
假设实现饮料冲泡流程。
类实现
// 抽象类 abstract class Beverage { // 模板方法 prepare(): void { this.boilWater(); this.brew(); this.pourInCup(); this.addCondiments(); } boilWater(): void { console.log("煮沸水"); } abstract brew(): void; // 抽象步骤 pourInCup(): void { console.log("倒入杯中"); } abstract addCondiments(): void; // 抽象步骤 } // 具体子类:咖啡 class Coffee extends Beverage { brew(): void { console.log("冲泡咖啡"); } addCondiments(): void { console.log("加糖和奶"); } } // 具体子类:茶 class Tea extends Beverage { brew(): void { console.log("浸泡茶叶"); } addCondiments(): void { console.log("加柠檬"); } } // 使用 const coffee = new Coffee(); coffee.prepare(); // 输出:煮沸水 冲泡咖啡 倒入杯中 加糖和奶 const tea = new Tea(); tea.prepare(); // 输出:煮沸水 浸泡茶叶 倒入杯中 加柠檬
prepare() 是模板方法,固定流程。子类重写 brew() 和 addCondiments(),不改整体结构。
函数式实现
// ====================== // Step 1: 定义策略函数类型 // ====================== type BrewFunction = () => void; type AddCondimentsFunction = () => void; // ====================== // Step 2: 固定步骤函数 // ====================== const boilWater = (): void => { console.log("🔥 Boiling water..."); }; const pourInCup = (): void => { console.log("🥛 Pouring into cup..."); }; // ====================== // Step 3: 模板方法(高阶函数) // ====================== const makeDrink = (brew: BrewFunction, addCondiments: AddCondimentsFunction) => (): void => { console.log("n🧪 Starting to make a drink...n"); boilWater(); // 固定 brew(); // 可变 pourInCup(); // 固定 addCondiments(); // 可变 console.log("n✅ Drink is ready!n"); }; // ====================== // Step 4: 策略实现(不同饮料) // ====================== // Coffee const brewCoffee: BrewFunction = () => { console.log("☕ Brewing coffee grounds..."); }; const addSugarAndMilk: AddCondimentsFunction = () => { console.log("🍬 Adding sugar and milk..."); }; // Tea const brewTea: BrewFunction = () => { console.log("🍵 Steeping the tea..."); }; const addLemon: AddCondimentsFunction = () => { console.log("🍋 Adding a slice of lemon..."); }; // ====================== // Step 5: 组合并执行 // ====================== const makeCoffee = makeDrink(brewCoffee, addSugarAndMilk); makeCoffee(); const makeTea = makeDrink(brewTea, addLemon); makeTea();
真实案例
下面列举了 3 个真实开源仓库,包含明确的“模板方法模式(Template Method Pattern)”逻辑。
Apache Kafka(Java)
Kafka 的 复制(Replica / Fetcher / LogCleaner)流程大量使用模板方法模式。
模板方法骨架,文件:AbstractFetcherThread.java
public abstract class AbstractFetcherThread extends ShutdownableThread { @Override public void doWork() { Map<TopicPartition, FetchData> fetched = fetchData(); // 模板步骤 processFetchedData(fetched); // 模板步骤 maybeThrottle(); } protected abstract Map<TopicPartition, FetchData> fetchData(); protected abstract void processFetchedData(Map<TopicPartition, FetchData> fetched); }
子类实现步骤,示例:ReplicaFetcherThread.java
@Override protected Map<TopicPartition, FetchData> fetchData() { // 从 leader 拉取日志 } @Override protected void processFetchedData(Map<TopicPartition, FetchData> fetched) { // 写入本地日志副本 }
- doWork() 是固定流程(骨架)
- fetchData()、processFetchedData() 由子类决定
Spring Framework / Spring AOP
Spring AOP 的拦截器链实现中大量使用模板方法结构。
模板方法骨架,文件:AbstractPlatformTransactionManager.java
public final TransactionStatus getTransaction(TransactionDefinition definition) { Object transaction = doGetTransaction(); // 模板步骤 boolean newTx = shouldStartTransaction(...); if (newTx) { doBegin(transaction, definition); // 模板步骤 } return prepareTransactionStatus(...); } protected abstract Object doGetTransaction(); protected abstract void doBegin(Object transaction, TransactionDefinition definition);
子类实现步骤,示例:DataSourceTransactionManager.java
@Override protected Object doGetTransaction() { return new DataSourceTransactionObject(); } @Override protected void doBegin(Object transaction, TransactionDefinition definition) { // 开启 JDBC 事务 }
getTransaction()定义统一事务开启流程- 某些步骤由子类实现(JDBC, JPA, Hibernate 都不一样)
Medusa.js
Medusa 是一个基于 TypeScript 的电商框架,批量任务、支付、库存策略等通过抽象基类强制子类实现步骤。
模板方法骨架,文件:AbstractBatchJobStrategy.ts
export abstract class AbstractBatchJobStrategy { async prepareBatchJob(batchJob: BatchJob): Promise<void> { await this.preProcessBatchJob(batchJob); // 模板步骤 await this.processJob(batchJob); // 模板步骤 await this.postProcessBatchJob(batchJob); // 模板步骤 } protected abstract preProcessBatchJob(job: BatchJob): Promise<void>; protected abstract processJob(job: BatchJob): Promise<void>; protected abstract postProcessBatchJob(job: BatchJob): Promise<void>; }
子类实现步骤,示例:ProductImportStrategy.ts
export class ProductImportStrategy extends AbstractBatchJobStrategy { protected async preProcessBatchJob(job) { ... } protected async processJob(job) { ... } protected async postProcessBatchJob(job) { ... } }
- 基类定义“批处理任务生命周期流程”
- 子类完成具体逻辑
结语
模板方法模式提升代码可维护性,适用于框架设计。