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

设计模式(二十三)行为型:模板方法模式详解

设计模式(二十三)行为型:模板方法模式详解

模板方法模式(Template Method Pattern)是 GoF 23 种设计模式中的行为型模式之一,其核心价值在于定义一个操作中的算法骨架,而将一些步骤延迟到子类中实现,使得子类可以在不改变算法结构的前提下重新定义算法的某些特定步骤。它通过“父类控制流程,子类实现细节”的方式,实现了代码复用与行为扩展的完美平衡。模板方法模式是构建框架、标准化流程、实现钩子机制、统一处理逻辑(如数据处理、构建流程、业务审批流)的基石,是“好莱坞原则”(Don’t call us, we’ll call you)在面向对象设计中的经典体现。

一、详细介绍

模板方法模式解决的是“多个类实现同一算法,算法结构相同但某些步骤的具体实现不同,且需要防止子类改变整体流程”的问题。在传统设计中,每个类可能都实现完整的算法,导致大量重复代码;或者使用条件分支,导致逻辑混乱。模板方法模式通过抽象类定义算法的固定骨架(模板方法),将可变步骤声明为抽象方法或钩子方法,由子类实现

该模式包含以下核心角色:

  • AbstractClass(抽象类):定义算法的骨架(模板方法),该方法通常是一个 final 方法,防止子类覆盖。它包含:
    • 模板方法(Template Method):定义算法的步骤顺序,调用原语操作(Primitive Operations)。
    • 抽象原语操作(Abstract Primitive Operations):声明为 abstract,必须由子类实现,代表算法中可变的步骤。
    • 具体原语操作(Concrete Primitive Operations):在抽象类中提供默认实现,子类可选择性覆盖。
    • 钩子方法(Hook Methods):在抽象类中提供空实现或默认实现的 protected 方法,子类可选择性覆盖以“挂钩”到算法流程中,实现条件逻辑或扩展点。
  • ConcreteClass(具体子类):继承 AbstractClass,实现所有抽象原语操作,并可选择性覆盖具体原语操作或钩子方法,以定制算法的特定行为。

模板方法模式的关键优势:

  • 代码复用:算法骨架在父类中定义,避免重复。
  • 控制流程:父类控制算法的整体结构,防止子类破坏流程。
  • 扩展性:子类通过实现抽象方法或覆盖钩子来扩展行为。
  • 符合开闭原则:新增行为通过添加新子类实现,无需修改父类。
  • 标准化:强制所有子类遵循相同的算法流程。

与“策略模式”相比,策略模式在运行时通过组合选择算法,模板方法在编译时通过继承固定流程;策略更灵活,模板方法更强调流程控制。与“状态模式”相比,状态模式关注状态驱动的行为切换,模板方法关注流程中步骤的定制。与“命令模式”相比,命令封装请求,模板方法封装算法结构

模板方法模式适用于:

  • 框架设计(如 Spring MVC 的 AbstractController)。
  • 标准化业务流程(如订单处理、审批流)。
  • 构建工具(如编译、打包、部署流程)。
  • 数据处理管道(如 ETL 流程)。
  • 图形渲染流程。
  • 单元测试框架的 setUp/tearDown

二、模板方法模式的UML表示

以下是模板方法模式的标准 UML 类图:

extends
extends
«abstract»
AbstractClass
+templateMethod()
+concreteOperation()
+hookMethod()
+primitiveOperation1()
+primitiveOperation2()
ConcreteClassA
+primitiveOperation1()
+primitiveOperation2()
+hookMethod()
ConcreteClassB
+primitiveOperation1()
+primitiveOperation2()

图解说明

  • AbstractClass 定义 templateMethod()(通常为 final),它按固定顺序调用 primitiveOperation1(), primitiveOperation2(), concreteOperation(), hookMethod()
  • primitiveOperation1()primitiveOperation2() 是抽象方法,必须由子类实现。
  • concreteOperation() 在父类中有具体实现,子类可覆盖。
  • hookMethod() 是钩子,有默认实现(可能为空),子类可选择性覆盖以插入自定义逻辑。
  • ConcreteClassAConcreteClassB 实现抽象方法,并可选择覆盖其他方法。

三、一个简单的Java程序实例及其UML图

以下是一个跨平台软件构建流程的示例,包含编译、测试、打包、部署步骤,不同平台(Windows, Linux)实现不同。

Java 程序实例
// 抽象类:软件构建流程
abstract class SoftwareBuildProcess {// 模板方法:定义构建流程的骨架 (final 防止子类修改流程)public final void build() {System.out.println("🚀 开始构建流程...");checkoutCode();compile();runTests();// 钩子方法:子类可决定是否打包if (shouldPackage()) {packageApplication();}// 钩子方法:子类可决定是否部署if (shouldDeploy()) {deploy();}cleanup();System.out.println("✅ 构建流程完成。\n");}// 具体原语操作:在抽象类中实现,所有子类共享private void checkoutCode() {System.out.println("  🔁 1. 检出代码 (从版本控制系统)");// 模拟操作}// 具体原语操作:提供默认实现,子类可覆盖protected void cleanup() {System.out.println("  🧹 6. 清理临时文件 (默认实现)");// 默认清理逻辑}// 钩子方法:提供默认行为(true/false),子类可覆盖以控制流程protected boolean shouldPackage() {return true; // 默认打包}protected boolean shouldDeploy() {return false; // 默认不部署}// 抽象原语操作:必须由子类实现protected abstract void compile();protected abstract void runTests();protected abstract void packageApplication();protected abstract void deploy();
}// 具体子类:Windows 构建流程
class WindowsBuildProcess extends SoftwareBuildProcess {@Overrideprotected void compile() {System.out.println("  ⚙️  2. 在 Windows 上编译 (使用 MSVC)");// 调用 Windows 编译器}@Overrideprotected void runTests() {System.out.println("  🧪 3. 运行 Windows 测试套件");// 执行 Windows 测试}@Overrideprotected void packageApplication() {System.out.println("  📦 4. 打包为 Windows 安装程序 (.exe)");// 生成 .exe 安装包}@Overrideprotected void deploy() {System.out.println("  🚀 5. 部署到 Windows 服务器");// 部署逻辑}// 覆盖钩子:Windows 构建默认不部署@Overrideprotected boolean shouldDeploy() {return false;}// 覆盖具体方法:Windows 特定的清理@Overrideprotected void cleanup() {System.out.println("  🧹 6. 清理 Windows 临时文件和 .obj 文件");// Windows 清理逻辑}
}// 具体子类:Linux 构建流程
class LinuxBuildProcess extends SoftwareBuildProcess {@Overrideprotected void compile() {System.out.println("  ⚙️  2. 在 Linux 上编译 (使用 GCC)");// 调用 GCC 编译器}@Overrideprotected void runTests() {System.out.println("  🧪 3. 运行 Linux 测试套件");// 执行 Linux 测试}@Overrideprotected void packageApplication() {System.out.println("  📦 4. 打包为 Linux 包 (.deb 或 .rpm)");// 生成 .deb 包}@Overrideprotected void deploy() {System.out.println("  🚀 5. 部署到 Linux 服务器 (使用 SSH)");// 部署逻辑}// 覆盖钩子:Linux 构建在 CI 环境中自动部署@Overrideprotected boolean shouldDeploy() {// 可根据环境变量决定return System.getenv("CI_ENV") != null;}// 覆盖钩子:Linux 构建时,如果测试失败则不打包@Overrideprotected boolean shouldPackage() {// 简化:假设测试总是通过return true;}
}// 具体子类:快速构建(跳过测试和打包,仅用于开发)
class QuickBuildProcess extends SoftwareBuildProcess {@Overrideprotected void compile() {System.out.println("  ⚡ 2. 快速编译 (仅编译修改的文件)");// 快速编译逻辑}@Overrideprotected void runTests() {System.out.println("  ⚠️  3. 跳过测试 (开发模式)");// 不运行测试}@Overrideprotected void packageApplication() {System.out.println("  ⚠️  4. 跳过打包 (开发模式)");// 不打包}@Overrideprotected void deploy() {System.out.println("  ⚠️  5. 跳过部署 (开发模式)");// 不部署}// 覆盖钩子:快速构建不打包@Overrideprotected boolean shouldPackage() {return false;}// 覆盖钩子:快速构建不部署@Overrideprotected boolean shouldDeploy() {return false;}
}// 客户端使用示例
public class TemplateMethodPatternDemo {public static void main(String[] args) {System.out.println("🏗️  跨平台软件构建系统 - 模板方法模式示例\n");// 构建 Windows 版本System.out.println("--- 构建 Windows 版本 ---");SoftwareBuildProcess windowsBuild = new WindowsBuildProcess();windowsBuild.build();// 构建 Linux 版本System.out.println("--- 构建 Linux 版本 ---");SoftwareBuildProcess linuxBuild = new LinuxBuildProcess();// 模拟 CI 环境System.setProperty("CI_ENV", "true");linuxBuild.build();// 开发者快速构建System.out.println("--- 开发者快速构建 ---");SoftwareBuildProcess quickBuild = new QuickBuildProcess();quickBuild.build();}
}
实例对应的UML图(简化版)
extends
extends
extends
«abstract»
SoftwareBuildProcess
+build()
-checkoutCode()
+cleanup()
+shouldPackage()
+shouldDeploy()
+compile()
+runTests()
+packageApplication()
+deploy()
WindowsBuildProcess
+compile()
+runTests()
+packageApplication()
+deploy()
+cleanup()
+shouldDeploy()
LinuxBuildProcess
+compile()
+runTests()
+packageApplication()
+deploy()
+shouldPackage()
+shouldDeploy()
QuickBuildProcess
+compile()
+runTests()
+packageApplication()
+deploy()
+shouldPackage()
+shouldDeploy()

运行说明

  • SoftwareBuildProcess 定义 build() 模板方法,固定流程:检出 -> 编译 -> 测试 -> (条件打包) -> (条件部署) -> 清理。
  • compile(), runTests(), packageApplication(), deploy() 是抽象方法,由子类实现。
  • cleanup() 是具体方法,有默认实现,子类可覆盖。
  • shouldPackage()shouldDeploy() 是钩子方法,子类可覆盖以控制流程分支。
  • WindowsBuildProcess, LinuxBuildProcess, QuickBuildProcess 实现各自平台的细节,并通过覆盖钩子定制流程。

四、总结

特性说明
核心目的定义算法骨架,延迟步骤实现到子类
实现机制抽象类定义模板方法(final),子类实现抽象方法
优点代码复用、控制流程、符合开闭原则、标准化流程、支持钩子扩展
缺点依赖继承(灵活性低于组合)、类爆炸(过多子类)、父类改动影响所有子类
适用场景框架设计、标准化流程、构建脚本、数据处理管道、业务审批流
不适用场景流程不固定、需要运行时动态组合行为、避免继承的场景

模板方法模式使用建议

  • 模板方法通常声明为 final,防止子类破坏流程。
  • 钩子方法是强大的扩展点,用于条件逻辑或可选步骤。
  • 可结合“工厂方法模式”创建具体子类。
  • 在 Java 中,Comparatorcompare() 可视为一种函数式模板方法。

架构师洞见:
模板方法模式是“框架设计”的灵魂。在现代架构中,其思想已演变为框架与库的根本区别:库是“你调用我”,框架是“我调用你”(好莱坞原则)。Spring 框架的 JdbcTemplate, RestTemplate 是其典型应用;JUnit 的 @Before, @After 是钩子方法;Servlet 的 doGet(), doPost() 是模板方法的变体;构建工具(Maven, Gradle)的生命周期是模板方法的宏观体现。

未来趋势是:模板方法将与函数式编程融合,模板方法接受函数式接口作为步骤(如 Java 8 的 Consumer, Function);在低代码/无代码平台中,可视化流程设计器生成模板方法代码;在AI 工作流中,AI Agent 的“规划-执行-反思”循环可建模为模板方法;在量子软件中,量子算法的通用步骤(初始化、操作、测量)可定义为模板。

掌握模板方法模式,是设计可复用框架、标准化系统的核心能力。作为架构师,应在设计任何需要“统一流程、定制细节”的模块时,优先考虑模板方法模式。它不仅是模式,更是系统秩序的基石——它用不变的骨架约束变化的细节,用父类的权威保障流程的正确,用子类的自由激发实现的创新,从而构建出既稳定又灵活、既统一又多样的软件生态系统。

http://www.lryc.cn/news/602090.html

相关文章:

  • 常用设计模式系列(十四)—模板方法模式
  • 开源智能体-JoyAgent集成ollama私有化模型
  • C#与C++交互开发系列(二十六):构建跨语言共享缓存,实现键值对读写与数据同步(实践方案)
  • 基于百度 iframe 框架与语音解析服务的数字人交互系统实现
  • 元宇宙工厂前端新形态:Three.js与WebGL实现3D产线交互的轻量化之路
  • Python系统交互库全解析
  • CentOS 7 安装 dnsmasq 解决nginx无法指定dns的问题
  • 新能源行业B端极简设计:碳中和目标下的交互轻量化实践
  • GitLab 18.2 发布几十项与 DevSecOps 有关的功能,可升级体验【三】
  • Windows 系统分辨率切换** 与 **Qt4 无边框窗口管理机制** 的交互
  • 全面解析MySQL(4)——三大范式与联合查询实例教程
  • Deep Learning_ Foundations and Concepts-Springer (2024)【拜读】前向编码器20章
  • 【CSS】设置表格表头固定
  • 深度学习----视觉里程计
  • 工业场景工服识别准确率↑32%!陌讯多特征融合算法实战解析
  • STM32 usb HOST audio USB 音频设备 放音乐
  • Pandas 里的分箱操作
  • 负载均衡集群HAproxy
  • MCP工作原理
  • Java-泛型类的定义与使用
  • 浅谈面试中的递归算法
  • 【Linux】编辑器vim和编译器gcc/g++
  • 解析分区、挂载与块设备:Linux 存储管理核心命令详解
  • 近屿智能正式发布AI得贤招聘官的AI面试官智能体6.3版本:交付替代人类面试官的打分结果
  • 零基础学习性能测试第九章:全链路追踪-项目实操
  • Jenkins + SonarQube 从原理到实战一:基于 K8s 部署与使用(含中文插件与 Python 扫描)
  • 力扣1457. 二叉树中的伪回文路径
  • 力扣面试150(42/150)
  • 旧物回收小程序:科技赋能,让旧物回收焕发生机
  • 软件测试之功能测试