设计模式——模版方法设计模式(行为型)

article/2025/8/11 7:34:38

摘要

模版方法设计模式是一种行为型设计模式,定义了算法的步骤顺序和整体结构,将某些步骤的具体实现延迟到子类中。它通过抽象类定义模板方法,子类实现抽象步骤,实现代码复用和算法流程控制。该模式适用于有固定流程但部分步骤可变的场景,如业务流程控制等。

1. 模版设计模式定义

定义一个操作中的算法骨架(即步骤的顺序和整体结构),而将某些步骤的具体实现延迟到子类中。子类可以在不改变算法结构的前提下,重新定义算法中的某些具体步骤。

1.1.1. 模版设计模式的关键点

  • 抽象模版类:定义一个模板方法,描述算法的整体流程。模板方法一般是 final,防止子类改变算法结构。
  • 基本方法(基本操作):模板方法所依赖的步骤,这些步骤可以是抽象的,也可以有默认实现。
  • 具体子类:实现抽象类中的抽象步骤,完成具体的业务逻辑。

1.1.2. 作用

  • 复用代码:把不变的行为放在父类,变化的行为由子类实现,避免代码重复。
  • 控制算法流程:子类只需关注具体步骤实现,算法整体流程由父类控制,增强代码的可维护性和可扩展性。

2. 模版设计模式结构

  1. 抽象类 (Abstract­Class) 会声明作为算法步骤的方法, 以及依次调用它们的实际模板方法。 算法步骤可以被声明为 抽象类型, 也可以提供一些默认实现。
  2. 具体类 (Concrete­Class) 可以重写所有步骤, 但不能重写模板方法自身。

2.1. 模版设计模式类图

2.2. 模版设计模式时序图

3. 模版设计模式实现方式

3.1. 模版设计模式的实现方式核心在于:

  • 在抽象父类中定义一个模板方法(通常是final的),它规定了算法的执行顺序和骨架。
  • 模板方法调用若干个基本方法(步骤),其中部分基本方法是抽象的,由子类实现;部分基本方法可以有默认实现。
  • 子类继承抽象父类,实现抽象步骤,完成具体业务逻辑。

3.2. 模板设计模式实现步骤

  1. 创建抽象类(AbstractClass)
    • 定义模板方法templateMethod(),并用final修饰,防止子类重写改变流程。
    • 模板方法中按照固定步骤顺序调用基本操作。
    • 定义基本操作(抽象方法或具体方法),其中抽象方法由子类实现。
  1. 创建具体子类(ConcreteClass)
    • 继承抽象类,实现抽象的基本方法,完成具体业务。

3.3. 示例代码(Java)

// 抽象模板类
public abstract class AbstractTemplate {// 模板方法,定义固定流程,防止子类覆盖public final void templateMethod() {step1();step2();step3();}// 抽象基本操作,由子类实现protected abstract void step1();protected abstract void step2();// 具体基本操作,父类实现,子类可选择复写protected void step3() {System.out.println("默认实现步骤3");}
}// 具体子类A
public class ConcreteTemplateA extends AbstractTemplate {@Overrideprotected void step1() {System.out.println("ConcreteTemplateA 实现步骤1");}@Overrideprotected void step2() {System.out.println("ConcreteTemplateA 实现步骤2");}
}// 具体子类B
public class ConcreteTemplateB extends AbstractTemplate {@Overrideprotected void step1() {System.out.println("ConcreteTemplateB 实现步骤1");}@Overrideprotected void step2() {System.out.println("ConcreteTemplateB 实现步骤2");}// 可以覆盖父类默认实现@Overrideprotected void step3() {System.out.println("ConcreteTemplateB 重写步骤3");}
}

3.4. 模版模式示例

public class Client {public static void main(String[] args) {AbstractTemplate templateA = new ConcreteTemplateA();templateA.templateMethod();// 输出:// ConcreteTemplateA 实现步骤1// ConcreteTemplateA 实现步骤2// 默认实现步骤3AbstractTemplate templateB = new ConcreteTemplateB();templateB.templateMethod();// 输出:// ConcreteTemplateB 实现步骤1// ConcreteTemplateB 实现步骤2// ConcreteTemplateB 重写步骤3}
}

说明

  • 模板方法templateMethod()固定了整体流程,子类不能改变流程,只能重写步骤细节。
  • 这样保证了算法骨架不变,细节可变。

4. 模版设计模式适合场景

4.1. ✅ 适合使用模版设计模式的场景

场景

说明

多个子类有相同算法骨架

多个子类共享固定流程,只有具体步骤实现不同,便于代码复用和规范流程。

需要复用公共流程代码

将不变的算法结构封装在父类,避免重复代码,提升维护性。

需要统一控制算法执行顺序

模板方法定义执行顺序,防止子类随意改变流程,保证算法正确执行。

算法结构清晰、变化点集中

业务流程稳定,只有个别步骤需要子类实现,方便集中管理和扩展。

希望固定流程,允许步骤扩展

允许子类通过实现抽象步骤或覆盖钩子方法灵活扩展功能,而不破坏整体流程。

4.2. ❌ 不适合使用模版设计模式的场景

场景

原因

需要动态调整或拼装流程

模板方法流程固定,难以支持运行时动态改变步骤或流程组合。

继承层次过深,代码复杂

模板方法依赖继承,过多层次会导致系统复杂且难维护。

业务变化点不明显或过少

过度抽象导致代码冗余,简单业务用模版模式反而增加复杂度。

多维度变化且复杂

多个变化点分布在算法不同部分,模板方法难以灵活应对,策略模式或责任链模式更合适。

需要高度灵活、组合式的行为

模板方法结构静态,不适合高动态组合或插件式设计。

5. 模版设计模式实战示例

5.1. 场景描述

在金融风控中,不同风控策略的执行流程大致相同:

  1. 数据准备
  2. 规则校验
  3. 风控决策(通过/拒绝)
  4. 结果记录

不同风控策略的规则校验细节不同,适合用模板设计模式抽象固定流程,把校验逻辑由子类实现。

5.2. 项目结构示例(Spring Boot)

com.example.riskcontrol
├── RiskControlTemplate.java      // 抽象模板类
├── UserRiskControl.java          // 具体风控策略1
├── TransactionRiskControl.java   // 具体风控策略2
├── RiskControlService.java       // 调用客户端
└── SpringBootApplication.java    // 启动类

5.3. 抽象模板类 RiskControlTemplate

package com.example.riskcontrol;public abstract class RiskControlTemplate {// 模板方法,定义风控流程public final void executeRiskControl(String userId) {prepareData(userId);boolean passed = validateRules(userId);makeDecision(passed);recordResult(userId, passed);}// 准备数据,具体实现可重写,默认空实现protected void prepareData(String userId) {System.out.println("准备风控数据,用户ID:" + userId);}// 抽象规则校验步骤,由具体策略实现protected abstract boolean validateRules(String userId);// 风控决策步骤,固定流程private void makeDecision(boolean passed) {if (passed) {System.out.println("风控通过,继续后续流程");} else {System.out.println("风控拒绝,终止流程");}}// 记录风控结果,默认实现protected void recordResult(String userId, boolean passed) {System.out.println("记录风控结果,用户ID:" + userId + ", 结果:" + (passed ? "通过" : "拒绝"));}
}

5.4. 具体策略实现类

package com.example.riskcontrol;import org.springframework.stereotype.Component;@Component("userRiskControl")
public class UserRiskControl extends RiskControlTemplate {@Overrideprotected boolean validateRules(String userId) {System.out.println("执行用户维度的风控规则校验,用户ID:" + userId);// 简单示例,实际接入数据库或外部接口判断return userId.hashCode() % 2 == 0;  // 偶数通过,奇数拒绝}
}
package com.example.riskcontrol;import org.springframework.stereotype.Component;@Component("transactionRiskControl")
public class TransactionRiskControl extends RiskControlTemplate {@Overrideprotected boolean validateRules(String userId) {System.out.println("执行交易维度的风控规则校验,用户ID:" + userId);// 这里模拟判断交易风险return userId.length() > 5;  // 用户ID长度大于5通过}
}

5.5. 业务调用层 RiskControlService

package com.example.riskcontrol;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;import java.util.Map;@Service
public class RiskControlService {// 用Spring注解注入所有实现的模板,key为bean名字private final Map<String, RiskControlTemplate> riskControlMap;@Autowiredpublic RiskControlService(Map<String, RiskControlTemplate> riskControlMap) {this.riskControlMap = riskControlMap;}// 执行指定策略public void executeRiskControl(String strategyName, String userId) {RiskControlTemplate strategy = riskControlMap.get(strategyName);if (strategy == null) {throw new IllegalArgumentException("未找到对应风控策略:" + strategyName);}strategy.executeRiskControl(userId);}
}

5.6. Spring Boot 启动类

package com.example.riskcontrol;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplication
public class SpringBootRiskControlApplication implements CommandLineRunner {@Autowiredprivate RiskControlService riskControlService;public static void main(String[] args) {SpringApplication.run(SpringBootRiskControlApplication.class, args);}@Overridepublic void run(String... args) throws Exception {System.out.println("模拟执行用户风控策略:");riskControlService.executeRiskControl("userRiskControl", "user12345");System.out.println("\n模拟执行交易风控策略:");riskControlService.executeRiskControl("transactionRiskControl", "user12345");}
}

5.7. 运行结果示例

模拟执行用户风控策略:
准备风控数据,用户ID:user12345
执行用户维度的风控规则校验,用户ID:user12345
风控拒绝,终止流程
记录风控结果,用户ID:user12345, 结果:拒绝模拟执行交易风控策略:
准备风控数据,用户ID:user12345
执行交易维度的风控规则校验,用户ID:user12345
风控通过,继续后续流程
记录风控结果,用户ID:user12345, 结果:通过

5.8. 模版模式总结

  • 抽象父类 RiskControlTemplate 封装公共流程(模板方法)。
  • 具体策略类只需实现风控规则校验步骤。
  • 通过 Spring 的 @Component 注解和自动装配 Map<String, RiskControlTemplate>,方便策略的灵活管理和调用。

6. 模版设计模式思考

6.1. 模版设计模式是不是用于父子类?

是的,模板设计模式(Template Method Pattern)确实是基于父子类继承关系实现的设计模式

6.1.1. 关键点总结:

  • 父类(抽象类):定义一个模板方法,规定算法的整体流程和执行顺序。模板方法通常是 final,防止子类改变流程。
  • 子类(具体类):继承父类,实现父类中定义的抽象步骤,完成具体业务逻辑。

换句话说,模板模式就是把不变的流程写在父类里,把可变的步骤留给子类实现。

6.1.2. 为什么是父子类?

  • 模板方法模式的核心就是“复用公共代码,且允许子类重写部分行为”,这是继承的典型应用场景。
  • 父类定义了算法框架,子类只实现细节,满足“开闭原则”(对扩展开放,对修改关闭)。

6.1.3. 举个简单类比:

  • 父类像“烘焙蛋糕的流程”
  • 子类像“不同口味蛋糕的具体做法”(巧克力、草莓等)

父类确定做蛋糕的步骤(比如准备材料、搅拌、烘焙、装饰),子类决定每步的具体实现。

模板设计模式在实战开发中常和以下设计模式配合使用,发挥协同优势:

6.2. 模版设计模式常和哪些模式用于实战开发中?

设计模式

结合方式及应用场景

策略模式

模板模式定义算法骨架,策略模式封装可替换的具体行为,实现灵活的步骤替换。比如模板方法中调用策略接口完成某步骤。

工厂方法模式

用工厂方法创建模板方法中需要的具体实现对象,解耦模板和具体子类的实例化。

钩子方法(Hook Method)

模板方法模式中提供可选的“钩子”方法,允许子类决定是否覆盖,灵活控制流程细节。

装饰器模式

在模板方法执行前后动态增强功能,如日志、权限校验等,避免修改模板代码。

责任链模式

将模板方法中的步骤拆分成责任链上的多个处理对象,形成更灵活的处理流程。

命令模式

模板方法中调用命令对象完成某些具体操作,命令模式封装请求,增强扩展性。

观察者模式

模板方法执行过程中发生重要事件时通知观察者,实现业务解耦。

简单示例场景

  • 金融风控:模板定义风控流程,策略模式封装不同风控规则。
  • Web请求处理:模板方法定义请求处理流程,工厂方法创建具体处理器。
  • 消息发送:模板定义消息发送步骤,装饰器动态添加日志或限流。

博文参考

  • 模板方法设计模式
  • 设计模式之模板方法模式 | DESIGN

http://www.hkcw.cn/article/aFLgwZpmeR.shtml

相关文章

ACL基础配置

文章目录 基本ACL配置组网需求组网拓扑实验步骤测试结果配置文件 高级ACL配置组网需求组网拓扑实验步骤测试结果配置文件 基本ACL配置 组网需求 现组网结构如下&#xff0c;VPC充当服务器&#xff0c;PC3与PC4是两个不同的网段&#xff0c;实现拒绝192.168.1.0/24访问VPC 组…

Redis最佳实践——热点数据缓存详解

Redis在电商热点数据缓存中的最佳实践 一、热点数据定义与识别 1. 热点数据特征 高频访问&#xff08;QPS > 1000&#xff09;数据规模适中&#xff08;单条 < 10KB&#xff09;数据变化频率低&#xff08;更新间隔 > 5分钟&#xff09;业务关键性高&#xff08;直接…

Git初识Git安装

目录 1. Git初识 1.1 提出问题 1.2 如何解决--版本控制器 1.3 注意事项 2 Git安装 2.1 Centos 2.2 Ubuntu 2.3 Windows 1. Git初识 1.1 提出问题 不知道你工作或学习时&#xff0c;有没有遇到这样的情况&#xff1a;我们在编写各种文档时&#xff0c;为了防止文档丢失…

数据库原理 试卷

以下是某高校教学管理系统的毕业论文指导ER图&#xff0c;数据信息&#xff1a;一名教师指导多名学生&#xff0c;一名学生只能选择一名教师&#xff0c;试分析完成以下各题&#xff0c;如用SQL命令完成的&#xff0c;在SQL Server2008验证后把答案写在题目的下方。 图1 毕业论…

在线音乐平台测试报告

一、项目背景 1.1 测试目标 验证音乐播放器功能完整性&#xff0c;确保用户管理、音乐管理、播放控制、收藏功能等核心模块符合需求。 1.2 项目技术栈 后端&#xff1a;Spring Boot/Spring MVC 数据库&#xff1a;MySQL 前端&#xff1a;原生 HTML/CSS/AJAX 1.3 项目源码 …

基于GeoTools和OSM路网求解两条道路相交点-以长沙市为例

目录 前言 一、基础数据简介 1、QGIS数据展示 2、元数据介绍 二、GeoTools相交求解 1、加载路网数据 2、查找道路信息 3、计算相交点 4、集成调用及输出 三、总结 前言 今天是端午节也是六一儿童节&#xff0c;当端午节碰到儿童节&#xff0c;双节的碰撞。在这祝各位朋…

中国高分辨率高质量地面CO数据集(2013-2023)

时间分辨率&#xff1a;日空间分辨率&#xff1a;1km - 10km共享方式&#xff1a;开放获取数据大小&#xff1a;9.83 GB数据时间范围&#xff1a;2013-01-01 — 2023-12-31元数据更新时间&#xff1a;2024-08-19 数据集摘要 ChinaHighCO数据集是中国高分辨率高质量近地表空气污…

t018-高校宣讲会管理系统 【含源码!】

项目演示视频 摘 要 传统办法管理信息首先需要花费的时间比较多&#xff0c;其次数据出错率比较高&#xff0c;而且对错误的数据进行更改也比较困难&#xff0c;最后&#xff0c;检索数据费事费力。因此&#xff0c;在计算机上安装高校宣讲会管理系统软件来发挥其高效地信息处…

NLP学习路线图(十四):词袋模型(Bag of Words)

在自然语言处理&#xff08;NLP&#xff09;的广阔天地中&#xff0c;词袋模型&#xff08;Bag of Words, BoW&#xff09; 宛如一块历经岁月沉淀的基石。它虽非当今最耀眼的明星&#xff0c;却为整个领域奠定了至关重要的基础&#xff0c;深刻影响了我们让计算机“理解”文本的…

Windows系统时间怎么设置

打开设置窗口&#xff1a;右键单击任务栏上的时间和日期显示区域&#xff0c;选择 “调整日期 / 时间”。 调整时区&#xff1a;在 “日期和时间” 设置窗口中&#xff0c;单击 “更改时区”&#xff0c;从下拉列表中选择正确的时区&#xff0c;若希望计算机自动调整为夏令时&a…

ssm 学习笔记day03

环境搭建 spring配置数据库 1.在pom.xml安装相应的依赖 2.在properties里面配置数据库的相关信息&#xff0c;需要强调的一点是&#xff0c;一定不要在properties里面添加任何空格&#xff0c;否则就会像我一样搞了两小时&#xff0c;数据一直报错&#xff0c;然后发现是空格的…

Python6.1打卡(day33)

DAY 33 MLP神经网络的训练 知识点回顾&#xff1a; 1.PyTorch和cuda的安装 2.查看显卡信息的命令行命令&#xff08;cmd中使用&#xff09; 3.cuda的检查 4.简单神经网络的流程 1.数据预处理&#xff08;归一化、转换成张量&#xff09; 2.模型的定义 …

python打卡day42

Grad-CAM与Hook函数 知识点回顾 回调函数lambda函数hook函数的模块钩子和张量钩子Grad-CAM的示例 在深度学习中&#xff0c;我们经常需要查看或修改模型中间层的输出或梯度&#xff0c;但标准的前向传播和反向传播过程通常是一个黑盒&#xff0c;很难直接访问中间层的信息。PyT…

[总结]前端性能指标分析、性能监控与分析、Lighthouse性能评分分析

前端性能分析大全 前端性能优化 LightHouse性能评分 性能指标监控分析 浏览器加载资源的全过程性能指标分析 性能指标 在实现性能监控前&#xff0c;先了解Web Vitals涉及的常见的性能指标 Web Vitals 是由 Google 推出的网页用户体验衡量指标体系&#xff0c;旨在帮助开发者量…

Linux 驱动之设备树

Linux 驱动之设备树 参考视频地址 【北京迅为】嵌入式学习之Linux驱动&#xff08;第七期_设备树_全新升级&#xff09;_基于RK3568_哔哩哔哩_bilibili 本章总领 1.设备树基本知识 什么是设备树&#xff1f; ​ Linux之父Linus Torvalds在2011年3月17日的ARM Linux邮件列表…

Unity Mono与IL2CPP比较

Unity提供了两种主要的脚本后端(Scripting Backend)选项&#xff1a;Mono和IL2CPP。它们在性能、平台支持和功能特性上有显著差异。 Edit>Project Settings>Player>Other Settings Mono后端 特点&#xff1a; 基于开源的Mono项目(.NET运行时实现) 使用即时编译(JIT…

配置Ollama环境变量,实现远程访问

在安装 Ollama 时配置环境变量 OLLAMA_HOST0.0.0.0:11434的主要目的是允许 Ollama 服务被局域网或远程设备访问&#xff0c;而不仅仅是本地主机&#xff08;localhost&#xff09;。 以下是详细原因&#xff1a; 1. Ollama默认行为的限制 默认情况下&#xff0c;Ollama 的 API…

仓颉鸿蒙开发:制作底部标签栏

今天制作标签栏&#xff0c;标签栏里面的有4个区域&#xff1a;首页、社区、消息、我的&#xff0c;以及对应的图标。点击的区域显示为高亮&#xff0c;未点击的区域显示为灰色 简单的将视图上面区域做一下 一、制作顶部公共视图部分 internal import ohos.base.* internal …

AWS之数据分析

目录 数据分析产品对比 1. Amazon Athena 3. AWS Lake Formation 4. AWS Glue 5. Amazon OpenSearch Service 6. Amazon Kinesis Data Analytics 7. Amazon Redshift 8.Amazon Redshift Spectrum 搜索服务对比 核心功能与定位对比 适用场景 关键差异总结 注意事项 …

Linux进程间通信----简易进程池实现

进程池的模拟实现 1.进程池的原理&#xff1a; 是什么 进程池是一种多进程编程模式&#xff0c;核心思想是先创建好一定数量的子进程用作当作资源&#xff0c;这些进程可以帮助完成任务并且重复利用&#xff0c;避免频繁的进程的创建和销毁的开销。 下面我们举例子来帮助理…