什么是模板方法模式?JavaScript 实现一个模板方法模式的示例
·
什么是模板方法模式?
模板方法模式(Template Method Pattern)是一种行为型设计模式,它定义一个操作的算法骨架(模板方法),将算法的一些步骤延迟到子类中实现。模板方法确保算法的结构不变,而子类可以按需重写具体步骤,从而在不改变算法整体流程的前提下,灵活定制部分细节。
其核心思想是 “封装不变,扩展可变”:将通用流程(不变部分)放在父类的模板方法中,将具体实现(可变部分)交给子类,既保证了算法的一致性,又允许子类灵活扩展。
模板方法模式的核心角色
- 抽象类(Abstract Class):定义算法的骨架(模板方法),包含多个抽象方法(步骤),由子类实现。模板方法通常用
final修饰(防止子类重写,JavaScript 中可通过命名规范约束)。 - 具体子类(Concrete Class):继承抽象类,实现抽象方法(步骤),完成算法中特定于子类的逻辑。
JavaScript 实现模板方法模式示例:泡茶与泡咖啡
以下以 “冲泡饮品” 为例,实现模板方法模式。场景是:泡茶和泡咖啡的流程相似(都需要 “烧开水→泡原料→倒入杯子→加调料”),但具体步骤的细节不同(如原料不同、调料不同)。通过模板方法模式,将通用流程放在父类,具体步骤由子类实现。
代码实现 - javascript
// -------------- 1. 抽象类:饮品冲泡模板 --------------
class Beverage {
// 模板方法:定义算法骨架(固定流程)
make() {
this.boilWater(); // 步骤1:烧开水(通用步骤)
this.brew(); // 步骤2:泡原料(子类实现)
this.pourInCup(); // 步骤3:倒入杯子(通用步骤)
this.addCondiments(); // 步骤4:加调料(子类实现)
}
// 通用步骤:烧开水(所有饮品都需要)
boilWater() {
console.log("烧开水(100℃)");
}
// 通用步骤:倒入杯子(所有饮品都需要)
pourInCup() {
console.log("将饮品倒入杯子");
}
// 抽象步骤:泡原料(由子类实现)
brew() {
throw new Error("子类必须实现 brew 方法");
}
// 抽象步骤:加调料(由子类实现)
addCondiments() {
throw new Error("子类必须实现 addCondiments 方法");
}
}
// -------------- 2. 具体子类:泡茶 --------------
class Tea extends Beverage {
// 实现泡原料步骤(茶)
brew() {
console.log("用热水浸泡茶叶");
}
// 实现加调料步骤(茶加柠檬)
addCondiments() {
console.log("加入柠檬");
}
}
// -------------- 3. 具体子类:泡咖啡 --------------
class Coffee extends Beverage {
// 实现泡原料步骤(咖啡)
brew() {
console.log("用热水冲泡咖啡粉");
}
// 实现加调料步骤(咖啡加牛奶和糖)
addCondiments() {
console.log("加入牛奶和糖");
}
}
// -------------- 4. 客户端使用 --------------
console.log("=== 泡茶流程 ===");
const tea = new Tea();
tea.make(); // 调用模板方法,执行完整流程
// 输出:
// 烧开水(100℃)
// 用热水浸泡茶叶
// 将饮品倒入杯子
// 加入柠檬
console.log("\n=== 泡咖啡流程 ===");
const coffee = new Coffee();
coffee.make(); // 调用相同的模板方法,执行不同细节
// 输出:
// 烧开水(100℃)
// 用热水冲泡咖啡粉
// 将饮品倒入杯子
// 加入牛奶和糖
代码说明
-
抽象类(Beverage):
- 模板方法
make()定义了冲泡饮品的固定流程:boilWater()→brew()→pourInCup()→addCondiments(),确保所有饮品遵循相同的步骤顺序。 - 通用步骤
boilWater()和pourInCup()在父类中实现(所有饮品共享)。 - 抽象步骤
brew()和addCondiments()由子类实现(不同饮品的差异点)。
- 模板方法
-
具体子类(Tea、Coffee):
- 继承
Beverage并实现抽象步骤:Tea重写brew()(泡茶叶)和addCondiments()(加柠檬);Coffee重写为 “冲泡咖啡粉” 和 “加牛奶糖”。 - 无需修改模板方法
make(),即可通过实现不同步骤完成定制化流程。
- 继承
-
核心优势:
- 流程统一:模板方法固定了算法结构,避免子类随意修改流程导致的不一致(如必须先烧开水再泡原料)。
- 代码复用:通用步骤(如烧开水)在父类中实现,子类无需重复编写。
- 灵活扩展:新增饮品(如奶茶)只需继承
Beverage并实现抽象步骤,符合 “开闭原则”。
带钩子方法的扩展示例 - javascript
模板方法模式中还可以加入 “钩子方法(Hook)”,允许子类在特定节点控制流程是否执行,进一步增强灵活性。例如,有些咖啡可能不加调料:
class BeverageWithHook extends Beverage {
// 钩子方法:默认返回 true(表示需要加调料)
needCondiments() {
return true;
}
// 重写模板方法,加入钩子判断
make() {
this.boilWater();
this.brew();
this.pourInCup();
if (this.needCondiments()) { // 根据钩子决定是否执行加调料步骤
this.addCondiments();
}
}
}
// 不加调料的黑咖啡
class BlackCoffee extends BeverageWithHook {
brew() {
console.log("用热水冲泡黑咖啡粉");
}
addCondiments() {
console.log("加入糖(但我不需要)"); // 不会执行
}
// 重写钩子:返回 false(不需要加调料)
needCondiments() {
return false;
}
}
console.log("\n=== 泡黑咖啡流程 ===");
const blackCoffee = new BlackCoffee();
blackCoffee.make();
// 输出:
// 烧开水(100℃)
// 用热水冲泡黑咖啡粉
// 将饮品倒入杯子
// (跳过加调料步骤)
钩子方法 needCondiments() 允许子类控制流程分支,使模板更加灵活。
模板方法模式的常见应用场景
- 框架设计:如前端框架的生命周期函数(React 的
componentDidMount、Vue 的mounted),框架定义生命周期流程(模板方法),开发者重写具体阶段的逻辑。 - 流程标准化:如报表生成(固定步骤:获取数据→处理数据→渲染报表)、测试用例(固定步骤:初始化→执行测试→清理)。
- 代码复用:当多个类的流程相似但细节不同时,用模板方法提取公共流程,减少重复代码。
模板方法模式的核心是 “骨架不变,细节可变”,通过父类定义规则、子类填充细节,实现了算法的一致性和灵活性的平衡,是框架设计中常用的模式。
更多推荐

所有评论(0)