Java设计模式-模板方法模式

Java设计模式-模板方法模式

模式概述

模板方法模式简介

核心思想:定义一个操作中的算法骨架(模板方法),将算法中某些步骤的具体实现延迟到子类中完成。子类可以在不改变算法整体结构的前提下,重定义这些步骤的行为,从而实现代码复用与扩展的平衡。

模式类型:行为型设计模式(关注对象间的交互与职责分配)。

作用

  • 复用公共逻辑:将多个子类共有的算法步骤提取到父类,避免代码重复。
  • 提高扩展性:子类仅需实现差异化的步骤,符合“开闭原则”(对扩展开放,对修改关闭)。
  • 规范流程:父类通过模板方法固定算法的整体结构,确保子类行为的一致性。

典型应用场景

  • 多个子类有公共的行为逻辑,但部分步骤实现不同(如数据库访问:连接、执行SQL、关闭连接的流程固定,但不同数据库的驱动实现不同)。
  • 框架中需要控制子类的执行流程(如Spring的JdbcTemplate封装了JDBC操作的通用流程,具体SQL执行由子类或回调实现)。
  • 需要约束子类的行为,确保关键步骤不被遗漏(如订单处理流程:下单→支付→发货→通知,其中支付方式可自定义)。

我认为:模板方法模式是“流程标准化”与“步骤定制化”的完美结合,父类搭骨架,子类填细节。

课程目标

  • 理解模板方法模式的核心思想和经典应用场景
  • 识别应用场景,使用模板方法模式解决功能要求
  • 了解模板方法模式的优缺点

核心组件

角色-职责表

角色职责示例类名
抽象模板角色定义模板方法(算法骨架)和基本方法(具体方法、抽象方法、钩子方法)AbstractBeverageMaker
具体模板角色继承抽象模板角色,实现所有抽象方法,并可选重写钩子方法CoffeeMakerTeaMaker

类图

下面是一个简化的类图表示,展示了模板方法模式中的主要角色及其交互方式:

继承
继承
«abstract»
AbstractBeverageMaker
+final void makeBeverage()
-void boilWater()
-abstract void brew()
-void pourInCup()
-boolean needAddCondiments()
-void addCondiments()
CoffeeMaker
+void brew()
+boolean needAddCondiments()
+void addCondiments()
TeaMaker
+void brew()

传统实现 VS 模板方法模式

案例需求

案例背景:实现饮料制作功能(如咖啡、茶),通用流程为:烧水→冲泡→倒入杯子→添加调料(可选)。不同饮料的冲泡方式(如咖啡粉 vs 茶叶)和调料添加(如加糖 vs 不加)不同。

传统实现(痛点版)

代码实现

// 传统实现:每个饮料独立编写完整流程
class CoffeeMaker {
    public void makeCoffee() {
        boilWater();  // 重复代码
        brewCoffee(); // 咖啡特有逻辑
        pourInCup();  // 重复代码
        addSugar();   // 咖啡特有逻辑
    }

    private void boilWater() {
        System.out.println("烧水:煮沸100℃");
    }

    private void brewCoffee() {
        System.out.println("冲泡:用热水冲咖啡粉");
    }

    private void pourInCup() {
        System.out.println("倒入杯子");
    }

    private void addSugar() {
        System.out.println("添加:糖和牛奶");
    }
}

class TeaMaker {
    public void makeTea() {
        boilWater();  // 重复代码
        brewTea();    // 茶叶特有逻辑
        pourInCup();  // 重复代码
        // 茶不需要调料,无需添加
    }

    private void boilWater() {
        System.out.println("烧水:煮沸100℃"); // 重复代码
    }

    private void brewTea() {
        System.out.println("冲泡:用热水泡茶叶");
    }

    private void pourInCup() {
        System.out.println("倒入杯子"); // 重复代码
    }
}

痛点总结

  • 代码冗余boilWater()pourInCup()等方法在每个子类中重复实现。
  • 扩展性差:新增饮料(如果汁)需复制大量重复代码,违反开闭原则。
  • 流程不可控:无法保证所有饮料遵循相同的基础流程(如漏掉“倒入杯子”步骤)。

模板方法模式 实现(优雅版)

代码实现

// 抽象模板角色:定义流程骨架
abstract class AbstractBeverageMaker {
    // 模板方法(final修饰,防止子类修改流程)
    public final void makeBeverage() {
        boilWater();
        brew();       // 调用抽象方法(子类实现)
        pourInCup();
        if (needAddCondiments()) {  // 调用钩子方法(控制是否添加调料)
            addCondiments();
        }
    }

    // 具体方法(通用逻辑)
    private void boilWater() {
        System.out.println("烧水:煮沸100℃");
    }

    // 抽象方法(子类必须实现)
    protected abstract void brew();

    // 具体方法(通用逻辑)
    private void pourInCup() {
        System.out.println("倒入杯子");
    }

    // 钩子方法(默认不添加调料,子类可选重写)
    protected boolean needAddCondiments() {
        return false;
    }

    // 钩子方法关联的具体操作(子类可选重写)
    protected void addCondiments() {
        // 默认空实现
    }
}

// 具体模板角色:咖啡制作
class CoffeeMaker extends AbstractBeverageMaker {
    @Override
    protected void brew() {
        System.out.println("冲泡:用热水冲咖啡粉");
    }

    @Override
    protected boolean needAddCondiments() {
        return true;  // 咖啡需要添加调料
    }

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

// 具体模板角色:茶叶制作
class TeaMaker extends AbstractBeverageMaker {
    @Override
    protected void brew() {
        System.out.println("冲泡:用热水泡茶叶");
    }

    // 不重写needAddCondiments(),默认不添加调料
}

优势

  • 消除冗余:公共方法(如boilWater())在抽象类中实现,子类无需重复。
  • 流程可控:模板方法通过final修饰,确保子类无法修改基础流程。
  • 灵活扩展:子类仅需实现抽象方法(如brew()),并通过钩子方法(needAddCondiments())控制可选逻辑。

局限

  • 类数量增加:每个差异化的子类需单独定义,可能增加系统复杂度。
  • 抽象类设计成本:需合理规划抽象方法与钩子方法,过度设计可能导致冗余。

模式变体

  • 具体模板方法:将模板方法声明为final,禁止子类修改算法骨架(强制遵循固定流程)。
  • 钩子方法(Hook Method):提供默认实现的方法(通常返回布尔值或空操作),子类可选择是否重写以影响模板方法的行为(如上述案例中的needAddCondiments())。
  • 参数化模板:在模板方法中添加参数,允许子类通过参数调整行为(如数据库操作模板支持传入事务隔离级别)。

最佳实践

建议理由
抽象类保持稳定模板方法模式的核心是流程固定,频繁修改抽象类会导致所有子类连锁改动。
钩子方法提供默认实现减少子类必须重写的负担,仅当需要差异化时才覆盖。
避免过度抽象若子类间差异极小(如仅有1个步骤不同),可能更适合直接继承而非模板模式。
模板方法用final修饰防止子类意外修改算法骨架,确保流程一致性。

一句话总结

模板方法模式通过“父类定义流程骨架,子类实现差异化步骤”,在保证流程规范的同时,实现了代码复用与灵活扩展。

如果关注Java设计模式内容,可以查阅作者的其他Java设计模式系列文章。😊

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值