设计模式——模版方法设计模式(行为型)
摘要
模版方法设计模式是一种行为型设计模式,定义了算法的步骤顺序和整体结构,将某些步骤的具体实现延迟到子类中。它通过抽象类定义模板方法,子类实现抽象步骤,实现代码复用和算法流程控制。该模式适用于有固定流程但部分步骤可变的场景,如业务流程控制等。
1. 模版设计模式定义
定义一个操作中的算法骨架(即步骤的顺序和整体结构),而将某些步骤的具体实现延迟到子类中。子类可以在不改变算法结构的前提下,重新定义算法中的某些具体步骤。
1.1.1. 模版设计模式的关键点
- 抽象模版类:定义一个模板方法,描述算法的整体流程。模板方法一般是
final
,防止子类改变算法结构。 - 基本方法(基本操作):模板方法所依赖的步骤,这些步骤可以是抽象的,也可以有默认实现。
- 具体子类:实现抽象类中的抽象步骤,完成具体的业务逻辑。
1.1.2. 作用
- 复用代码:把不变的行为放在父类,变化的行为由子类实现,避免代码重复。
- 控制算法流程:子类只需关注具体步骤实现,算法整体流程由父类控制,增强代码的可维护性和可扩展性。
2. 模版设计模式结构
- 抽象类 (AbstractClass) 会声明作为算法步骤的方法, 以及依次调用它们的实际模板方法。 算法步骤可以被声明为
抽象
类型, 也可以提供一些默认实现。 - 具体类 (ConcreteClass) 可以重写所有步骤, 但不能重写模板方法自身。
2.1. 模版设计模式类图
2.2. 模版设计模式时序图
3. 模版设计模式实现方式
3.1. 模版设计模式的实现方式核心在于:
- 在抽象父类中定义一个模板方法(通常是
final
的),它规定了算法的执行顺序和骨架。 - 模板方法调用若干个基本方法(步骤),其中部分基本方法是抽象的,由子类实现;部分基本方法可以有默认实现。
- 子类继承抽象父类,实现抽象步骤,完成具体业务逻辑。
3.2. 模板设计模式实现步骤
- 创建抽象类(AbstractClass)
-
- 定义模板方法
templateMethod()
,并用final
修饰,防止子类重写改变流程。 - 模板方法中按照固定步骤顺序调用基本操作。
- 定义基本操作(抽象方法或具体方法),其中抽象方法由子类实现。
- 定义模板方法
- 创建具体子类(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. 场景描述
在金融风控中,不同风控策略的执行流程大致相同:
- 数据准备
- 规则校验
- 风控决策(通过/拒绝)
- 结果记录
不同风控策略的规则校验细节不同,适合用模板设计模式抽象固定流程,把校验逻辑由子类实现。
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
相关文章:

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

Deepin 20.9社区版安装Docker
个人博客地址:Deepin 20.9社区版安装Docker | 一张假钞的真实世界 注意事项 Deepin 20.9 社区版安装 Docker 需要注意两点: 因为某些原因,Docker 官方源基本不可用,所以需要使用镜像源进行安装。当然也可以用安装包直接安装&am…...
Node.js 全栈技术栈的开发者,Web3 面试题
作为一名熟悉 Node.js 全栈技术栈的开发者,在面试 Web3 岗位时,通常会被问到涵盖 区块链原理、智能合约开发、安全性、前后端集成、常用库/协议等方面的问题。下面是我为你整理的 Web3 开发方向面试题清单,尤其适合有 Node.js 背景的全栈工程…...

纯数据挖掘也能发Microbiome?
抗生素滥用导致多重耐药微生物在全球蔓延,但新型抗生素的研发进展缓慢,亟需找到替代抗生素的新型防御策略。抗菌肽(AMPs)作为天然防御分子,具有低耐药潜力和广谱活性。德国小蠊(Blattella germanica&#x…...

2025年05月30日Github流行趋势
项目名称:agenticSeek 项目地址url:https://github.com/Fosowl/agenticSeek项目语言:Python历史star数:13040今日star数:1864项目维护者:Fosowl, steveh8758, klimentij, ganeshnikhil, apps/copilot-pull-…...

跨平台猫咪桌宠 BongoCat v0.4.0 绿色版
—————【下 载 地 址】——————— 【本章下载一】:https://pan.xunlei.com/s/VORWH1a7lPhdwvon6DJgKvrNA1?pwdcw2h# 【本章下载二】:https://pan.quark.cn/s/c3ac86f4e296 【百款黑科技】:https://ucnygalh6wle.feishu.cn/wiki/…...
【课堂笔记】标签传播算法Label Propagation Algorithm(LPA)
文章目录 问题背景基本假设算法步骤数学原理解剖算法收敛性 问题背景 标签传播算法(Label Propagation Algorithm, LPA)主要解决的是数据标注不足的实际问题。在许多现实世界的机器学习任务中,获取大量标注数据(即带有正确标签的样…...

Dify案例实战之智能体应用构建(一)
一、部署dify Windows安装Docker部署dify,接入阿里云api-key进行rag测试-CSDN博客 可以参考我的前面文章,创建一个本地dify或者直接dify官网使用一样的(dify官网需要科学上网) 二、Dify案例实战之智能体 2.1 智能面试官 需求;…...

从模式到架构:Java 工厂模式的设计哲学与工程化实践
一、工厂模式概述 (一)定义与核心思想 工厂模式(Factory Pattern)是软件开发中常用的创建型设计模式,其核心思想是将对象的创建过程封装起来,通过工厂类来统一管理对象的创建逻辑。这种模式分离了对象的创…...

docker问题记录
docker pull镜像: 即使配置了镜像源也还是走的国外的镜像源: 解决办法:在pull镜像的时候强制走自己的镜像 比如:拉取rabbitmq,强制使用"https://docker.m.daocloud.io"这个镜像 docker pull docker.m.da…...

设计模式——代理设计模式(结构型)
摘要 本文详细介绍了代理设计模式,包括其定义、结构组成、实现方式、适用场景及实战示例。代理设计模式是一种结构型设计模式,通过代理对象控制对目标对象的访问,可增强功能或延迟加载等。文中通过类图、时序图、静态代理、JDK动态代理、CGL…...
Elasticsearch的集群管理介绍
Elasticsearch 集群管理是确保分布式环境下系统稳定运行、高可用和高性能的关键。以下从集群架构、节点类型、故障转移到监控优化,全面解析 Elasticsearch 集群管理的核心要点: 一、集群架构与节点类型 1. 基本概念 集群(Cluster):由一个或多个节点组成,共同存储数据并…...
Spring MVC + Tomcat 8.5 踩坑实录:Servlet 版本引发的部署失败
🚧 Spring MVC Tomcat 8.5 踩坑实录:Servlet 版本引发的部署失败 🌐 作者:劲爽小猴头 🗓️ 时间:2025-05-28 📚 关键词:Spring MVC、Tomcat、Servlet、WAR部署、web.xml、Maven、JD…...

从“固定“到“流动“:移动充电如何重塑用户体验?
在传统充电模式中,"固定"不仅是技术的特征,更成为用户行为的枷锁——人们需要规划行程、寻找插座、等待电量填满,这种被动适配正在被移动充电技术颠覆。当充电设备从墙面解放,化身可携带的能源胶囊,甚至嵌入…...

玩客云 OEC/OECT 笔记(1) 拆机刷入Armbian固件
目录 玩客云 OEC/OECT 笔记(1) 拆机刷入Armbian固件玩客云 OEC/OECT 笔记(2) 运行RKNN程序 外观 内部 PCB正面 PCB背面 PCB背面 RK3566 1Gbps PHY 配置 OEC 和 OECT(OEC-turbo) 都是基于瑞芯微 RK3566/RK3568 的网络盒子, 没有HDMI输入输出. 硬件上 OEC 和 OECT…...
docker环境添加安装包持久性更新
1、进入docker 环境 2、安装新的安装包 pip install XXXX3、不要退出docker,新开终端,给当前环境从新打包更新镜像 docker commit ad6e1d2c5869 mynewpythonimagead6e1d2c5869是上面运行中的容器id, docker images 查看mynewpythonimage是新…...

GIS数据类型综合解析
GIS数据类型综合解析 目录 GIS数据类型综合解析1. 总体介绍2. GIS数据类型分类与对比2.1 主要数据类型对比表 3. 详细解析与扩展内容3.1 矢量数据(Vector Data)3.2 栅格数据(Raster Data)3.3 属性数据(Attribute Data&…...
VR 汽车:引领生产与设计的革命性飞跃
在汽车生产设计环节,VR 技术同样发挥着不可替代的重要作用。回首过去,设计师们设计一款新车时,面临着极为繁琐且艰巨的任务。首先,要绘制海量的图纸,从车辆的整体轮廓到每一个零部件的精细构造,都需用精准的…...

Prometheus + Grafana 监控常用服务
一、引言 Prometheus监控常见服务的原理主要包括服务暴露指标和Prometheus抓取指标。一方面,被监控服务通过自身提供的监控接口或借助Exporter将服务的性能指标等数据以HTTP协议的方式暴露出来;另一方面,Prometheus根据配置好的采集任务&…...

6月1日星期日今日早报简报微语报早读
6月1日星期日,农历五月初六,早报#微语早读。 1、10个省份城镇化率超70%,广东城镇人口超9700万; 2、长沙居民起诉太平财险不赔“新冠险”,立案878天后获胜判; 3、海口:全市范围内禁止投放互联…...
盲盒经济2.0:数字藏品开箱是否适用赌博法规
首席数据官高鹏律师团队编著 一、年轻人的“盲盒信仰”,法律的灰色地带 近年来,“盲盒经济”从实体玩具扩展到数字藏品领域,掀起了一波全民开箱热潮。年轻人在社交平台上晒出“开箱暴击”的喜悦,平台方则以“限量发行”“价值赋…...

如何在 Ubuntu 24.04 服务器上安装 Apache Solr
Apache Solr 是一个免费、开源的搜索平台,广泛应用于实时索引。其强大的可扩展性和容错能力使其在高流量互联网场景下表现优异。 Solr 基于 Java 开发,提供了分布式索引、复制、负载均衡及自动故障转移和恢复等功能。 本教程将指导您如何在 Ubuntu 24.…...

unity编辑器扩展dll形式展示
1.背景:最近搞工程迁移发现一旦c#报错就会导致编辑器菜单没法使用,做了一些尝试发现使用dll的方式会是不错的选择。当然有些工具还是建议用外部的c#工程来写比如winform. 2.遇到的问题:我记得之前2017年左右的时候做一个unity的dll工程并不需…...

vscode中launch.json、tasks.json的作用及实例
文章目录 launch.json是什么作用多环境调试简单实例进阶使用核心配置项解析调试第三方程序 launch.json是什么 顾名思义:它是在.vscode文件夹下的launch.json,所以是vscode启动调试的配置文件。总结:通过定义调试参数、环境变量和启动方式&a…...
UI自动化测试中的元素等待机制解析
目录 一、显式等待机制 二、隐式等待机制 三、强制等待机制 等待策略对比指南 在UI自动化测试中,元素定位失败通常由两种原因导致:页面存在iframe框架或未合理设置等待机制。本文重点解析三种等待策略及其应用场景。 一、显式等待机制 核心原理 通过…...

VScode编译调试debug,gpu的cuda程序,Nsight
进行下面操作的前提是,我们的环境已经能跑简单的CUDA程序了。 一、安装Nsight 二、创建launch.json文件 {"version": "0.2.0","configurations": [{"name": "CUDA C: Launch","type": "cuda-gdb…...

中企出海大会|打造全球化云计算一张网,云网络助力中企出海和AI创新
全球化是阿里云的长期战略,未来阿里云将持续加大云和 AI 基础设施建设投入。首先是加速打造全球化的云计算网络,一张具备 AI技术服务能力和全球竞争力的云计算网络是阿里云的长期目标。 —— 阿里巴巴集团 CEO、阿里云智能集团董事长兼 CEO 吴泳铭 5 月 …...
qwen-0.5b小模型的用处和显存要求
详细分析一下 Qwen-0.5B (5亿参数) 这个模型在不同训练阶段的显存需求以及它的用途。(根据网页反馈:1、0.5b做蒸馏,特定领域轻松超越sft的7b;2、大部分实时要求高的业务需要用小模型初筛降量,比如意图识别;…...

防范DDoS攻击,服务器稳定性崩溃的根源与高效防御对策
DDoS攻击(分布式拒绝服务攻击)已成为危害服务器稳定性和业务连续性的主要因素之一。本文将深入探讨为什么服务器一遇到DDoS攻击就崩溃,以及如何从根本上实现有效防御和应对这一威胁,帮助企业提升网络安全水平。 具体内容如下&…...

深入理解 SELinux:通过 Nginx 和 SSH 服务配置实践安全上下文与端口策略
目录 一、引言 二、实验环境说明 三、实验 1:Nginx 服务安全上下文配置 3.1 实验目标 3.2 操作步骤 1. 开启 SELinux 并重启系统 2. 安装 Nginx 并创建自定义目录 3. 配置 Nginx 指向自定义目录 4. 分析 SELinux 上下文冲突 5. 修改上下文为合法类型 6. 验…...