设计模式之模板模式(Template Pattern)详解
在软件开发中,我们常常遇到**“流程固定但部分步骤实现不同”**的场景:例如,泡茶和泡咖啡的流程都包含“煮水、冲泡、加调料”等步骤,但“冲泡的原料”和“添加的调料”因饮品而异;又如,框架中数据库操作的流程(建立连接→执行SQL→处理结果)固定,但“SQL语句”和“结果处理逻辑”因业务而异。
模板模式(Template Pattern)正是为解决这类问题而生的行为型设计模式。它的核心思想是:定义算法的骨架(模板方法),将可变步骤延迟到子类实现,从而在不改变流程结构的前提下,灵活扩展具体步骤。
目录#
- 模板模式的核心概念
- 模板模式的结构与角色
- 代码示例:从“泡茶”到“泡咖啡”
- 应用场景:框架、游戏与业务系统
- 优缺点分析
- 最佳实践:如何优雅使用模板模式?
- 与其他模式的对比(策略模式、工厂方法)
- 总结
1. 模板模式的核心概念#
模板模式的核心是:“固定流程骨架,扩展可变步骤”。
- 它定义一个算法的整体流程(模板方法),并将流程中可变的步骤抽象为方法,由子类实现。
- 子类只需关注“可变步骤”的具体实现,无需关心整体流程,从而实现流程复用与步骤扩展的平衡。
2. 模板模式的结构与角色#
模板模式包含3个核心角色:
(1)抽象模板类(Abstract Class)#
- 定义模板方法(Template Method):封装算法的整体流程(如
prepareRecipe()),通常用final修饰(防止子类重写,保证流程稳定)。 - 定义基本方法:
- 抽象方法(Abstract Method):流程中必须由子类实现的可变步骤(如
brew()、addCondiments())。 - 钩子方法(Hook Method):流程中可选扩展的步骤,通常提供默认实现(如
customerWantsCondiments()),子类可重写以控制流程分支。
- 抽象方法(Abstract Method):流程中必须由子类实现的可变步骤(如
(2)具体子类(Concrete Class)#
- 继承抽象模板类,实现抽象方法或重写钩子方法,完成可变步骤的定制。
(3)客户端(Client)#
- 调用模板方法,触发算法流程。
3. 代码示例:从“泡茶”到“泡咖啡”#
我们用“制作热饮”的经典场景,演示模板模式的实现:
(1)抽象模板类:BeverageMaker#
// 抽象模板类:定义热饮制作的流程骨架
public abstract class BeverageMaker {
// 模板方法:固定热饮制作的整体流程(final修饰,防止子类重写)
public final void prepareRecipe() {
boilWater(); // 固定步骤:煮水
brew(); // 可变步骤:冲泡(子类实现)
pourInCup(); // 固定步骤:倒入杯子
if (customerWantsCondiments()) { // 钩子方法:是否加调料(可选扩展)
addCondiments(); // 可变步骤:加调料(子类实现)
}
}
// 固定步骤:煮水(所有热饮通用,父类实现)
private void boilWater() {
System.out.println("煮水...");
}
// 固定步骤:倒入杯子(所有热饮通用,父类实现)
private void pourInCup() {
System.out.println("倒入杯子...");
}
// 抽象方法:冲泡(子类必须实现)
protected abstract void brew();
// 抽象方法:加调料(子类必须实现)
protected abstract void addCondiments();
// 钩子方法:是否需要加调料?默认返回true(子类可重写)
protected boolean customerWantsCondiments() {
return true;
}
}(2)具体子类1:Tea(泡茶)#
public class Tea extends BeverageMaker {
@Override
protected void brew() {
System.out.println("用沸水冲泡茶叶...");
}
@Override
protected void addCondiments() {
System.out.println("添加柠檬...");
}
// 重写钩子方法:当前用户不需要加调料
@Override
protected boolean customerWantsCondiments() {
return false;
}
}(3)具体子类2:Coffee(泡咖啡)#
public class Coffee extends BeverageMaker {
@Override
protected void brew() {
System.out.println("用沸水冲泡咖啡粉...");
}
@Override
protected void addCondiments() {
System.out.println("添加牛奶和糖...");
}
// 使用默认钩子方法(加调料)
}(4)测试类:Client#
public class Client {
public static void main(String[] args) {
// 制作茶
BeverageMaker tea = new Tea();
tea.prepareRecipe();
System.out.println("--- 分割线 ---");
// 制作咖啡
BeverageMaker coffee = new Coffee();
coffee.prepareRecipe();
}
}输出结果:#
煮水...
用沸水冲泡茶叶...
倒入杯子...
--- 分割线 ---
煮水...
用沸水冲泡咖啡粉...
倒入杯子...
添加牛奶和糖...
代码解析:#
prepareRecipe()是模板方法,固定了“煮水→冲泡→倒入→加调料(可选)”的流程。brew()和addCondiments()是抽象方法,由子类(Tea、Coffee)实现具体逻辑。customerWantsCondiments()是钩子方法:Tea重写后关闭了“加调料”步骤,Coffee使用默认实现(加调料)。
4. 应用场景:框架、游戏与业务系统#
模板模式在以下场景中广泛应用:
(1)框架开发:流程固定,细节扩展#
- Spring的JdbcTemplate:固定“获取连接→执行SQL→处理结果→关闭连接”的流程,“SQL语句”和“结果映射”由用户(子类/回调)实现。
- Servlet的doGet/doPost:
service()是模板方法,固定“解析请求→调用doGet/doPost→生成响应”的流程,业务逻辑由doGet()/doPost()实现。
(2)游戏开发:角色创建流程#
- 游戏中“创建角色”的流程:
选择种族→选择职业→分配属性→生成外观。其中“分配属性”和“生成外观”因种族/职业而异,可用模板模式固定流程,子类(如HumanWarriorCreator、ElfMageCreator)实现可变步骤。
(3)业务系统:文档生成#
- 报表生成工具:固定“读取数据→渲染模板→导出文件”的流程,“读取数据”(从DB/Excel)和“渲染模板”(PDF/Excel格式)由子类实现。
5. 优缺点分析#
优点:#
- 代码复用:模板方法固定流程,避免子类重复编写相同逻辑(如“煮水”“倒入杯子”)。
- 扩展性强:子类只需关注“可变步骤”,无需关心整体流程,灵活扩展。
- 开闭原则:对扩展开放(子类新增步骤实现),对修改关闭(模板方法的流程无需修改)。
缺点:#
- 耦合性高:抽象类与子类高度耦合,子类的修改可能影响整体流程。
- 维护成本:若流程步骤过多,抽象类会变得复杂,子类逻辑难以追踪。
- 灵活性限制:模板方法的流程是固定的,若需大幅调整流程,需修改抽象类(违反开闭原则)。
6. 最佳实践:如何优雅使用模板模式?#
(1)合理拆分步骤:抽象方法 + 钩子方法#
- 抽象方法:用于流程中必须定制的核心步骤(如
brew())。 - 钩子方法:用于流程中可选扩展的步骤(如
customerWantsCondiments()),提供默认实现,子类按需重写。
(2)模板方法用final修饰#
- 模板方法(如
prepareRecipe())用final修饰,防止子类重写,破坏流程稳定性。
(3)避免“步骤爆炸”#
- 若流程步骤过多(如超过5个可变步骤),需拆分抽象类,或结合策略模式封装部分步骤,降低复杂度。
(4)钩子方法的妙用#
- 钩子方法可返回布尔值(如
isDebugMode())、对象(如getConfig()),用于控制流程分支或传递配置。
7. 与其他模式的对比#
(1)模板模式 vs 策略模式#
- 模板模式:固定流程骨架,扩展步骤的实现(如“冲泡用茶叶还是咖啡粉”)。
- 策略模式:封装整个算法(如“排序用冒泡还是快排”),算法间可动态切换。
- 场景选择:流程固定、仅步骤细节不同 → 用模板模式;需动态切换整个算法 → 用策略模式。
(2)模板模式 vs 工厂方法模式#
- 工厂方法是模板模式的特殊情况:工厂方法的“创建对象”是模板方法中的一个步骤。
- 例如:
BeverageMaker的brew()可设计为工厂方法,由子类决定创建“茶叶”还是“咖啡粉”对象。
8. 总结#
模板模式的核心是**“固定流程,扩展步骤”,适用于“流程稳定、细节可变”**的场景(如框架流程、业务模板)。
- 优点:流程复用、扩展性强、符合开闭原则。
- 缺点:耦合度高、维护成本高、灵活性有限。
使用时需注意:
- 合理拆分抽象方法与钩子方法,避免步骤爆炸。
- 结合策略模式、工厂方法等,补充模板模式的灵活性。
参考文献#
- 《设计模式:可复用面向对象软件的基础》(GoF经典著作)
- 菜鸟教程:模板模式
- InfoQ:《设计模式之模板方法模式的实践》
通过本文的解析与示例,希望你能掌握模板模式的核心思想,并在实际项目中灵活运用,让代码既稳定又可扩展!
作者:XXX
日期:YYYY-MM-DD
版权声明:本文原创,转载请注明出处。