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

模板方法模式(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.pswp.cn/bicheng/91287.shtml
繁体地址,请注明出处:http://hk.pswp.cn/bicheng/91287.shtml
英文地址,请注明出处:http://en.pswp.cn/bicheng/91287.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Postgresql 查询使用正则

今天接到任务,要从数据库中查询数据,对于postgresql 我并不熟悉,问了百度,问了通义千问。发现Postgresql 在写query sql 的时候,可以使用正则匹配,不单是使用like 这种关键字。我像发现了新大陆一样的兴奋。…

【WRF-Chem Emissions教程第八期】转换实用程序

转换实用程序 8.1 将中间二进制文件转换为 WRF-Chem 数据文件 文件命名规范与风格 8.2 Binary data file format(中间二进制排放数据文件的格式和结构) FORTRAN 示例程序说明 8.3 Building the WRF-Chemistry emissions conversion code 编译步骤 验证编译结果 8.4 Namelist …

Qt Ribbon效果界面

实现效果&#xff1a;头文件&#xff1a;#pragma once #include <QMdiArea> #include <QMdiSubWindow> #include <QMainWindow> #include "ui_MainFrame1.h" #include "DockManager.h" #include "DockAreaWidget.h"class Main…

如何修改 MySQL 8.0 的密码,和忘记密码时如何修改

要修改 MySQL 8.0 的密码&#xff0c;可以通过以下几种方法实现&#xff1a;方法 1&#xff1a;使用 ALTER USER 命令&#xff08;推荐&#xff09;这是 MySQL 8.0 推荐的修改密码方式&#xff1a;-- 修改当前登录用户的密码 ALTER USER USER() IDENTIFIED BY 新密码;-- 修改指…

图像处理控件Aspose.Imaging教程:使用 C# 编程将 CMX 转换为 PNG

PNG图像文件格式是广泛使用的图像格式之一。这种图像文件格式提供了增强的共享和显示功能。另一方面&#xff0c;CMX也是 Corel 应用程序主要使用的图像文件格式。然而&#xff0c;将 CMX 转换为 PNG 可以帮助用户在网络上查看和共享文件。因此&#xff0c;在本指南中&#xff…

迪丽热巴写真壁纸

下载&#xff1a;https://pan.quark.cn/s/a740dbac8274迪丽热巴绝美写真&#xff0c;高清壁纸展现独特魅力&#xff0c;每一张都是视觉盛宴

C++11 std::function 详解:通用多态函数包装器

在C11标准中&#xff0c;引入了std::function这一通用多态函数包装器&#xff0c;定义于<functional>头文件中。它彻底改变了C中函数对象的使用方式&#xff0c;为不同类型的可调用实体提供了统一的接口。std::function能够存储、复制和调用任何可复制构造的可调用目标&a…

Kafka运维实战 16 - kafka 分区重新分配【实战】

💻 Kafka运维实战 (17篇) 📝Kafka运维实战 17 - kafka 分区副本从 1 增加到 3【实战】 📝Kafka运维实战 16 - kafka 分区重新分配【实战】 📝Kafka运维实战 15 - kafka 重设消费者组位移入门和实战【实战】 📝Kafka运维实战 14 - kafka消费者组消费进度(Lag)深入理…

智汇AI,应用领航 | 华宇万象问数入选2025全景赋能典型案例

7月29日&#xff0c;以“AI城市&#xff1a;数启新纪元”为主题中关村人工智能与未来城市论坛在中关村国家自主创新示范区展示中心举办。本次论坛围绕人工智能创新应用落地实践、新型数据基础设施建设、数据要素价值释放机制、城市智能治理等关键议题&#xff0c;邀请院士专家和…

sqli-labs:Less-7关卡详细解析

1. 思路&#x1f680; 本关的SQL语句为&#xff1a; $sql"SELECT * FROM users WHERE id(($id)) LIMIT 0,1";注入类型&#xff1a;字符串型&#xff08;单引号、双括号包裹&#xff09;提示&#xff1a;参数id需以))闭合 同样无法像常规一样回显&#xff0c;php输出语…

编程算法:从理论基石到产业变革的核心驱动力

文章目录 算法的本质与效率衡量 基础算法范式的实践价值 排序算法的演进与选择 动态规划的实用技巧 算法在现代技术栈中的应用 大数据处理的算法框架 编译器中的算法优化 算法驱动的产业变革 金融领域的算法应用 医疗健康领域的算法创新 制造业的算法优化 算法的未来趋势 结语 …

深度学习中的注意力机制:原理、应用与未来展望

在人工智能领域&#xff0c;深度学习技术已经取得了巨大的突破&#xff0c;而注意力机制&#xff08;Attention Mechanism&#xff09;作为深度学习中的一个重要概念&#xff0c;正在逐渐改变我们对模型的理解和应用。本文将深入探讨注意力机制的原理、在不同领域的应用以及未来…

LeetCode 4:寻找两个正序数组的中位数

LeetCode 4&#xff1a;寻找两个正序数组的中位数问题定义与核心挑战 给定两个有序&#xff08;升序&#xff09;数组 nums1 和 nums2&#xff0c;要求找到它们的中位数&#xff0c;且算法时间复杂度为 O(log(mn))&#xff08;m 和 n 分别是两个数组的长度&#xff09;。 中位数…

独立站如何吃掉平台蛋糕?DTC模式下的成本重构与利润跃升

一、成本结构革命&#xff1a;从「流量税」到「用户终身价值」亚马逊卖家需支付15%佣金12%广告费&#xff0c;导致每$100收入中平台抽成$27。而成熟独立站通过SEO&#xff08;自然流量占比超40%&#xff09;和社交媒体内容引流&#xff0c;将获客成本压缩至$8-$15。更关键的是用…

应用驱动 协同创新:中国人工智能开启高质量发展新篇章

人工智能技术的突破性发展正引发全球产业格局的深刻变革。在2025年这个关键节点&#xff0c;中国以"应用导向"为战略支点&#xff0c;依托新型举国体制优势&#xff0c;正在构建具有中国特色的人工智能发展体系&#xff0c;为全球智能革命贡献东方智慧。一、战略布局…

ZKMall商城开源本地部署指南

1. 开发环境配置 以下是开发工具的最低版本要求。在继续之前&#xff0c;请务必安装所有必需的依赖项。 工具版本JDK17MySQL5.7.3Redis5.0Maven3.9.5NodeJS20.18.0 1.1 安装资源 如需详细的安装指南&#xff0c;您可以参考以下教程&#xff1a; JDK: 菜鸟教程 Java 环境搭建…

《使用Qt Quick从零构建AI螺丝瑕疵检测系统》——8. AI赋能(下):在Qt中部署YOLOv8模型

目录一、概述1.1 背景介绍&#xff1a;从“训练”到“部署”1.2 学习目标二、在C中集成ONNX模型2.1 准备模型文件2.2 修改Backend以加载和运行模型三、关键一步&#xff1a;输出结果的后处理四、运行与验证五、总结与展望一、概述 1.1 背景介绍&#xff1a;从“训练”到“部署…

【动态规划 | 多状态问题】动态规划求解多状态问题

算法相关知识点可以通过点击以下链接进行学习一起加油&#xff01;斐波那契数列模型路径问题多状态问题通常涉及多个决策点和状态转换&#xff0c;解决起来复杂且计算量大。动态规划作为一种强大的算法工具&#xff0c;能够通过将问题分解为子问题并逐步求解&#xff0c;显著提…

【HTTP】防XSS+SQL注入:自定义HttpMessageConverter过滤链深度解决方案

防XSSSQL注入&#xff1a;自定义HttpMessageConverter过滤链深度解决方案一、安全威胁模型分析二、自定义HttpMessageConverter架构设计2.1 技术栈组成三、完整实现代码3.1 安全过滤工具类3.2 自定义HttpMessageConverter3.3 Spring安全配置四、深度防御增强方案4.1 SQL注入参数…

学习游戏制作记录(冻结敌人时间与黑洞技能)7.30

1.实现剑击中敌人时冻结敌人时间Enemy脚本&#xff1a;public float defaultMoveSpeed;//默认速度defaultMoveSpeed moveSpeed;//Awake&#xff08;&#xff09;中设置public virtual void FreezeTime(bool _timeFreeze)//冻结设置函数{if (_timeFreeze){moveSpeed 0;anim.sp…