设计模式——模板模式

什么是模板模式?

模板设计模式(Template Method Pattern)是一种行为型设计模式,其核心定义为:定义一个算法的骨架流程(模板方法),将算法中某些步骤的具体实现延迟到子类中,使得子类可以在不改变算法整体结构的前提下,重新定义该算法的某些特定步骤。 简单来说,它通过抽象类定义了"做事情的步骤框架",其中包含:

  • 一个或多个模板方法 (通常用final修饰,防止子类修改流程),用于规定步骤的执行顺序;
  • 一些基本方法(可以是抽象方法、具体方法或钩子方法),其中抽象方法由子类实现具体逻辑,具体方法提供公共实现,钩子方法(可选)允许子类选择性地调整流程。
  • 本质是"固定骨架,变化细节",既保证了算法结构的一致性,又允许子类灵活定制特定步骤的实现。

适用场景

  1. 流程标准化,但细节差异化

当多个业务逻辑拥有相同的执行步骤框架,但部分步骤的具体实现因业务类型不同而有差异时。 例如:

  • 框架中的生命周期管理(如Servlet的init()service()destroy()流程,具体业务由子类实现service());
  • 文档生成工具(固定步骤:"收集数据→格式化内容→输出文档",不同文档类型(PDF/Word)的"格式化"步骤不同);
  • 测试框架(固定流程:"前置准备(setup)→执行测试(runTest)→清理资源(teardown)",具体测试逻辑由子类实现)。
  1. 需要控制子类的算法结构

当希望严格约束子类的执行流程,避免子类随意修改核心步骤顺序时。 例如:支付流程(固定步骤:"验证参数→调用支付接口→记录日志→返回结果",子类只能修改"调用支付接口"的具体实现,不能改变步骤顺序)。

  1. 代码复用需求高

当多个子类存在大量重复代码(如公共步骤的实现),希望将重复逻辑抽取到父类中统一维护时。 例如:各类饮料制作("煮沸水""倒入杯子"是公共步骤,抽取到父类;"冲泡""加调料"由子类实现)。

举个例子

通过一个制作饮料的例子来展示模板设计模式的实现。 在这个例子中,我们将创建一个饮料制作的模板,定义制作饮料的基本步骤(煮沸水、冲泡、倒入杯子、添加调料),然后让具体的饮料(咖啡和茶)实现各自特有的步骤。 示例展示了模板设计模式的核心要素:

  1. 抽象类(Beverage)
  • 定义了模板方法(prepareRecipe()),该方法是final的,确保算法结构不被改变 - 包含抽象方法(brew()、addCondiments()),由子类实现
  • 包含具体方法(boilWater()、pourInCup()),提供公共实现
  • 包含钩子方法(customerWantsCondiments()),子类可以选择重写以改变算法行为
  • 代码如下:
java 复制代码
// 抽象类定义模板方法和基本步骤
public abstract class Beverage {
    // 模板方法,定义了制作饮料的算法骨架
    public final void prepareRecipe() {
        boilWater();
        brew();
        pourInCup();
        if (customerWantsCondiments()) {
            addCondiments();
        }
    }

    // 抽象方法,由子类实现具体的冲泡方式
    protected abstract void brew();

    // 抽象方法,由子类实现具体的添加调料方式
    protected abstract void addCondiments();

    // 具体方法,所有饮料制作都需要煮沸水
    private void boilWater() {
        System.out.println("煮沸水");
    }

    // 具体方法,所有饮料都需要倒入杯子
    private void pourInCup() {
        System.out.println("倒入杯子中");
    }

    // 钩子方法,子类可以重写来决定是否添加调料
    protected boolean customerWantsCondiments() {
        return true;
    }
}
  1. 具体子类(Coffee、Tea)
  • 实现抽象类中的抽象方法,提供特定于子类的实现
  • 可以选择重写钩子方法,改变模板方法的默认行为
  • 代码如下:
java 复制代码
// 咖啡类,实现模板中的抽象方法
public class Coffee extends Beverage {

    @Override
    protected void brew() {
        System.out.println("用沸水冲泡咖啡");
    }

    @Override
    protected void addCondiments() {
        System.out.println("添加糖和牛奶");
    }

    // 重写钩子方法,提供不同的行为
    @Override
    protected boolean customerWantsCondiments() {
        // 实际应用中这里可能会询问用户
        return true;
    }
}

// 茶类,实现模板中的抽象方法
public class Tea extends Beverage {

    @Override
    protected void brew() {
        System.out.println("用沸水浸泡茶叶");
    }

    @Override
    protected void addCondiments() {
        System.out.println("添加柠檬");
    }

    // 重写钩子方法,提供不同的行为
    @Override
    protected boolean customerWantsCondiments() {
        // 实际应用中这里可能会询问用户
        return false;
    }
}
  1. 客户端(Main)
  • 通过抽象类接口使用具体子类,不需要知道具体实现细节
  • 代码如下:
java 复制代码
// 测试类,演示模板模式的使用
public class Main {
    public static void main(String[] args) {
        System.out.println("制作咖啡:");
        Beverage coffee = new Coffee();
        coffee.prepareRecipe();

        System.out.println("\n制作茶:");
        Beverage tea = new Tea();
        tea.prepareRecipe();
    }
}

运行Main类会输出:

text 复制代码
制作咖啡:
煮沸水
用沸水冲泡咖啡
倒入杯子中
添加糖和牛奶

制作茶:
煮沸水
用沸水浸泡茶叶
倒入杯子中

可以看到,茶没有添加调料,因为它的钩子方法返回了false,这展示了钩子方法如何改变算法的流程。 参考上面这个示例,分析下优缺点。

优点

  1. 封装不变,扩展可变

父类定义固定的算法骨架(不变部分),子类仅需实现差异化步骤(可变部分),符合"开闭原则"(对扩展开放,对修改关闭)。

  1. 提高代码复用性

公共步骤在父类中实现,避免子类重复编写,减少冗余代码,便于维护(修改公共逻辑只需改父类)。

  1. 保证算法一致性

子类无法修改父类中模板方法的步骤顺序,确保所有子类遵循统一的算法结构,避免流程混乱。

  1. 简化子类实现

子类只需关注自身特有的步骤细节,无需关心整体流程,降低开发难度。

缺点

  1. 类数量增加

每一个具体实现都需要定义一个子类,若业务场景复杂,可能导致类数量激增,增加系统复杂度。

  1. 灵活性受限

算法骨架被父类固定,若需调整步骤顺序或新增步骤,必须修改父类,可能影响所有子类(违反"开闭原则"的修改限制)。

  1. 子类依赖父类

子类的实现与父类的模板方法紧密耦合,若父类修改抽象方法(如新增抽象步骤),所有子类都必须同步修改,维护成本较高。

  1. 可能导致过度设计

若业务流程简单且变化少,使用模板模式可能引入不必要的抽象层次,增加理解难度。

总结

模板设计模式适合流程固定、细节多变的场景,能有效提升代码复用性和一致性,但需权衡其带来的类数量增加和灵活性限制。在使用时,应确保流程确实具备稳定性,避免为简单场景过度设计。

相关推荐
极光雨雨7 小时前
【设计模式】模板方法模式
设计模式·模板方法模式
nVisual1 天前
运维新纪元:告别Excel手工规划,实现“零误差”决策
运维·网络·设计模式·自动化
喝拿铁写前端2 天前
Vue 实战:构建灵活可维护的菜单系统
前端·vue.js·设计模式
VisuperviReborn2 天前
打造自己的前端监控---前端流量监控
前端·设计模式·架构
BUG收容所所长2 天前
发布订阅模式 vs 观察者模式:它们真的是一回事吗?
前端·javascript·设计模式
探索为何2 天前
Transformer:从神坛到笑坛的华丽转身
设计模式·程序员·代码规范
AlenLi2 天前
JavaScript - 单例模式的几种简单实现方式
设计模式
心月狐的流火号2 天前
观察者模式解析与Spring事件机制
spring·设计模式
用户6120414922132 天前
C语言做的汽车线路模拟查询系统
c语言·后端·设计模式