设计模式-模板方法模式
要理解设计模式中的模板方法模式,我们可以从其核心思想入手:定义一个算法的骨架(模板),将算法中某些步骤的具体实现延迟到子类中,使得子类可以在不改变算法整体结构的情况下,重写特定步骤的实现。它的本质是“固定流程、灵活细节”,通过抽象父类把控整体逻辑,子类专注于个性化实现。
一、模板方法模式的核心角色
模板方法模式包含2个核心角色,各自职责如下:
| 角色 | 职责描述 |
|---|---|
| 抽象类(Abstract Class) | 定义算法的骨架(模板方法),包含一系列步骤方法(如抽象方法、具体方法、钩子方法)。模板方法通常用final修饰,防止子类修改算法结构。 |
| 具体子类(Concrete Class) | 继承抽象类,实现抽象类中声明的抽象方法(即算法中需要个性化的步骤),也可选择重写钩子方法(用于控制算法流程)。 |
二、Java代码演示
我们以“制作热饮(泡茶和泡咖啡)”为例(两者流程相似:烧开水→冲泡→倒杯子→加调料,但“冲泡”和“加调料”的具体方式不同),演示模板方法模式的实现。
1. 定义抽象类(Abstract Class)
封装热饮制作的通用流程(模板方法),并声明需要子类实现的抽象步骤:
// 抽象类:热饮制作器
public abstract class HotDrinkMaker {
// 模板方法:定义制作热饮的算法骨架(用final修饰,防止子类修改流程)
public final void makeDrink() {
boilWater(); // 步骤1:烧开水(通用步骤)
brew(); // 步骤2:冲泡(子类实现)
pourInCup(); // 步骤3:倒入杯子(通用步骤)
// 钩子方法:判断是否需要加调料(默认需要,子类可重写)
if (wantCondiments()) {
addCondiments(); // 步骤4:加调料(子类实现)
}
System.out.println("热饮制作完成!\n");
}
// 具体方法:烧开水(所有热饮通用,父类实现)
private void boilWater() {
System.out.println("步骤1:烧开水(100℃)");
}
// 具体方法:倒入杯子(所有热饮通用,父类实现)
private void pourInCup() {
System.out.println("步骤3:将热饮倒入杯子");
}
// 抽象方法:冲泡(子类必须实现,不同热饮方式不同)
protected abstract void brew();
// 抽象方法:加调料(子类必须实现,不同热饮调料不同)
protected abstract void addCondiments();
// 钩子方法:是否需要加调料(默认返回true,子类可重写以改变流程)
protected boolean wantCondiments() {
return true;
}
}
2. 定义具体子类(Concrete Class)
实现抽象类中的抽象方法,根据自身特性定制“冲泡”和“加调料”步骤,可选重写钩子方法:
// 具体子类1:泡茶
public class TeaMaker extends HotDrinkMaker {
@Override
protected void brew() {
System.out.println("步骤2:用沸水浸泡茶叶");
}
@Override
protected void addCondiments() {
System.out.println("步骤4:加入柠檬片");
}
// 可选:重写钩子方法(例如:有些人喝茶不加调料)
@Override
protected boolean wantCondiments() {
// 这里简化逻辑,固定返回true(实际可根据用户输入判断)
return true;
}
}
// 具体子类2:泡咖啡
public class CoffeeMaker extends HotDrinkMaker {
@Override
protected void brew() {
System.out.println("步骤2:用沸水冲泡咖啡粉");
}
@Override
protected void addCondiments() {
System.out.println("步骤4:加入牛奶和糖");
}
// 不重写钩子方法,使用父类默认实现(加调料)
}
// 具体子类3:黑咖啡(不加调料,重写钩子方法)
public class BlackCoffeeMaker extends HotDrinkMaker {
@Override
protected void brew() {
System.out.println("步骤2:用沸水冲泡黑咖啡粉");
}
@Override
protected void addCondiments() {
// 即使实现了该方法,由于钩子方法返回false,也不会被调用
System.out.println("步骤4:加入...(但不会执行)");
}
// 重写钩子方法:黑咖啡不加调料
@Override
protected boolean wantCondiments() {
return false;
}
}
3. 客户端(Client)测试
调用模板方法,观察不同热饮的制作流程:
public class Client {
public static void main(String[] args) {
// 制作茶
System.out.println("=== 制作茶 ===");
HotDrinkMaker tea = new TeaMaker();
tea.makeDrink();
// 制作咖啡
System.out.println("=== 制作咖啡 ===");
HotDrinkMaker coffee = new CoffeeMaker();
coffee.makeDrink();
// 制作黑咖啡(不加调料)
System.out.println("=== 制作黑咖啡 ===");
HotDrinkMaker blackCoffee = new BlackCoffeeMaker();
blackCoffee.makeDrink();
}
}
输出结果:
=== 制作茶 ===
步骤1:烧开水(100℃)
步骤2:用沸水浸泡茶叶
步骤3:将热饮倒入杯子
步骤4:加入柠檬片
热饮制作完成!
=== 制作咖啡 ===
步骤1:烧开水(100℃)
步骤2:用沸水冲泡咖啡粉
步骤3:将热饮倒入杯子
步骤4:加入牛奶和糖
热饮制作完成!
=== 制作黑咖啡 ===
步骤1:烧开水(100℃)
步骤2:用沸水冲泡黑咖啡粉
步骤3:将热饮倒入杯子
热饮制作完成!
三、模板方法模式的关键细节
- 模板方法:用
final修饰,确保算法结构不被子类修改,是整个模式的核心。 - 抽象方法:由子类实现,对应算法中“可变”的步骤(如
brew()、addCondiments())。 - 具体方法:由父类实现,对应算法中“不变”的通用步骤(如
boilWater()),子类可直接复用。 - 钩子方法:父类提供默认实现(如
wantCondiments()),子类可重写以控制算法流程(如是否执行某个步骤),增强灵活性。
四、模板方法模式的优点
- 固定流程,减少重复:将算法的通用步骤抽离到父类,避免子类重复编写,符合“干燥原则(DRY)”。
- 灵活扩展:子类只需关注自身特有的步骤实现,无需关心整体流程,符合“开闭原则”(新增功能只需新增子类)。
- 控制反转:父类定义“做什么”(流程),子类定义“怎么做”(细节),实现了对算法的集中管控。
通过这个例子,我们可以清晰看到:模板方法模式通过“骨架+细节”的分离,完美解决了“流程固定但步骤实现多变”的场景问题(如框架中的生命周期方法、报表生成、流程引擎等)。它是框架设计中常用的模式,能有效提升代码的复用性和可维护性。

浙公网安备 33010602011771号