当前位置: 首页 > backend >正文

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 {@Overrideprotected void brew() {System.out.println("冲泡:用热水冲咖啡粉");}@Overrideprotected boolean needAddCondiments() {return true;  // 咖啡需要添加调料}@Overrideprotected void addCondiments() {System.out.println("添加:糖和牛奶");}
}// 具体模板角色:茶叶制作
class TeaMaker extends AbstractBeverageMaker {@Overrideprotected void brew() {System.out.println("冲泡:用热水泡茶叶");}// 不重写needAddCondiments(),默认不添加调料
}

优势

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

局限

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

模式变体

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

最佳实践

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

一句话总结

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

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

http://www.xdnf.cn/news/18280.html

相关文章:

  • 常见开源协议详解:哪些行为被允许?哪些被限制?
  • B站 韩顺平 笔记 (Day 24)
  • K8S-Secret资源对象
  • 学习数组①
  • 1.Shell脚本修炼手册之---为什么要学Shell编程?
  • 【MySQL的卸载】
  • 读《精益数据分析》:规模化(Scale)—— 复制成功,进军新市场
  • PiscCode集成Hand Landmarker:实现高精度手部姿态检测与分析
  • JVM面试精选 20 题(终)
  • 【北京迅为】iTOP-4412精英版使用手册-第三十二章 网络通信-TCP套字节
  • 30.Linux cobbler自动化部署
  • 基于51单片机自动浇花1602液晶显示设计
  • STM32_0001 KEILMDK V5.36 编译一个STM32F103C8T6说core_cm3.h文件找不到以及编译器版本不匹配的解决办法
  • 多模型创意视频生成平台
  • 设计模式1-单例模式
  • PyTorch如何修改模型(魔改)?/替换模型,一般除了注意输入输出一致,还有其他要修改的吗?
  • 【Python】新手入门:python面向对象编程的三大特性是什么?python继承、封装、多态的特性都有哪些?
  • IT运维背锅权限泄露?集中式管控如何化解风险?
  • postman+newman+jenkins接口自动化
  • 次短路P2865 [USACO06NOV] Roadblocks G题解
  • cobbler
  • 换根DP(P3478 [POI 2008] STA-StationP3574 [POI 2014] FAR-FarmCraft)
  • Linux I/O 多路复用实战:深入剖析 Select 与 Poll
  • 在 Ubuntu Linux LTS 上安装 SimpleScreenRecorder 以录制屏幕
  • GPT-5 上线风波深度复盘:从口碑两极到策略调整,OpenAI 的变与不变
  • Jupyter Notebook 的终极进化:VS Code vs PyCharm,数据科学的IDE王者之争
  • 全球首款 8K 全景无人机影翎 A1 发布解读:航拍进入“先飞行后取景”时代
  • 扩展卡尔曼滤波(EKF)的一阶泰勒展开(雅可比矩阵)详解
  • 8 月中 汇报下近半个月都在做些什么
  • E10自定义统一认证+人员同步