【设计模式】模板方法与策略模式的结合使用
文章目录
- 1. 概述
- 1.1.简述模板方法
- 2.模板方法实现
- 2.1.简单实现
- 2.2.在SpringBoot中的实现
- 3.模板方法与策略模式的结合使用
- 3.1.代码实现
- 4.总结
1. 概述
模板方法是一种非常简单的设计模式,只要能够理解面向对象中的继承与多态就能够理解这种设计模式,我们可以在很多的框架源码中找到它的身影。
同时在我们的日常开发中,它一般是用在同类型且不同实现方式的业务逻辑中,抽取公共逻辑,简单的说就是,模板方法经常和策略模式结合使用。
本篇后续的代码中会涉及到策略模式,如果还不太熟悉策略模式使用实例的同学,可以先看一下上一篇文章:《SpringBoot优雅使用策略模式》
1.1.简述模板方法
模板方法通过继承来实现,顶层是一个抽象类,用于封装通用函数,并提供一个或多个抽象方法,下层是多个实现类,用于实现不同的业务逻辑分支,类图如下:
2.模板方法实现
实际使用的时候,一般会通过子类的实例调用父类中的模板方法templateMethod
,在模板方法中调用抽象方法,最终还是会调用到子类中覆写的实例方法,这是一种常见的钩子函数使用方式。
2.1.简单实现
- 抽象父类:
/*** 抽象父类*/ public abstract class BaseClass {final public void templateMethod() {System.out.println("执行模板方法");method1();method2();}abstract protected void method1();abstract protected void method2(); }
- 子类实现
/*** 子类1*/ public class ChildClass1 extends BaseClass {@Overrideprotected void method1() {System.out.println("执行子类1的方法1");}@Overrideprotected void method2() {System.out.println("执行子类1的方法2");} }
/*** 子类2*/ public class ChildClass2 extends BaseClass {@Overrideprotected void method1() {System.out.println("执行子类2的方法1");}@Overrideprotected void method2() {System.out.println("执行子类2的方法2");} }
简单的测试一下:
public static void main(String[] args) {BaseClass baseClass = new ChildClass1();baseClass.templateMethod();baseClass = new ChildClass2();baseClass.templateMethod();
}
打印出测试结果:
2.2.在SpringBoot中的实现
在使用SpringBoot进行开发的时候,我们通常不会手动去创建对象,而是将不同的已经创建好的bean
结合起来使用,我们可以在子类的对象中,通过@Autowired
将其他的bean
注入进来,并在运行时执行方法调用。
如果这个方法调用是通用的,我们可以将它抽取到的父类中去,但由于父类是抽象类无法实例化,自然也无法直接通过@Autowired
注入bean
,此时可以对子类做一点小改造,通过构造函数对父类进行赋值。
实现起来也非常的简单,下面是代码示例:
- 首先提供一个
QueryService
供子类注入import org.springframework.stereotype.Service;@Service public class QueryService {public void query() {System.out.println("执行查询方法");}}
- 其次需要再抽象父类中定义
QueryService
public abstract class BaseClass {protected QueryService queryService;final public void templateMethod() {System.out.println("执行模板方法");queryService.query();method1();}abstract protected void method1();}
- 最后,在子类中注入并对父类的
QueryService
赋值
import org.springframework.stereotype.Component;/*** 子类1*/
@Component
public class ChildClass1 extends BaseClass {public ChildClass1(QueryService queryService) {super.queryService = queryService;}@Overrideprotected void method1() {System.out.println("执行子类1的方法1");}}
/*** 子类2*/
@Component
public class ChildClass2 extends BaseClass {public ChildClass2(QueryService queryService) {super.queryService = queryService;}@Overrideprotected void method1() {System.out.println("执行子类2的方法1");}}
使用ApplicationContext
做一个简单的测试:
@Component
public class Test implements ApplicationContextAware {@Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {applicationContext.getBean(ChildClass1.class).templateMethod();applicationContext.getBean(ChildClass2.class).templateMethod();}
}
3.模板方法与策略模式的结合使用
模板方法与策略模式天然的可以结合使用,为了大家能够有个更直观的感受,我把两个类图放到一起,大家可以做一下对比。
相信大家也很容易可以看出来,如果我们现在业务中通过策略模式,让程序能够自行选择需要使用的子类实例,只需要再加上一个选择器就好了。
3.1.代码实现
在 上篇文章 中已经介绍了如何构建选择器,有需要的同学可以去看一下,在这里就不过多赘述,直接放实现代码。
只需要两个步骤就可以完成改造:
-
第一步,编写策略选择器与选择器枚举
import org.jetbrains.annotations.NotNull; import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.stereotype.Component;import java.util.Map; import java.util.stream.Collectors;/*** 策略选择器*/ @Component public class Selector implements ApplicationContextAware {private Map<String, BaseClass> selectorMap;public BaseClass select(String type) {return selectorMap.get(type);}@Overridepublic void setApplicationContext(@NotNull ApplicationContext applicationContext) throws BeansException {this.selectorMap = applicationContext.getBeansOfType(BaseClass.class).values().stream().filter(strategy -> strategy.getClass().isAnnotationPresent(SelectorAnno.class)).collect(Collectors.toMap(strategy -> strategy.getClass().getAnnotation(SelectorAnno.class).value(), strategy -> strategy));} }
import java.lang.annotation.*;/*** 选择器注解*/ @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE}) @Inherited @Documented public @interface SelectorAnno {String value();}
-
第二步:在两个子类中分别加入枚举
@Component @SelectorAnno("child1") public class ChildClass1 extends BaseClass{ }
@Component @SelectorAnno("child2") public class ChildClass2 extends BaseClass { }
改造完成之后,模拟一下调用端发起请求,做一个简单的测试:
import org.springframework.stereotype.Component;import javax.annotation.PostConstruct;@Component
public class Test {private final Selector selector;public Test(Selector selector) {this.selector = selector;}@PostConstructpublic void test() {// 模拟调用端传入策略标识this.doInvoke("child1");}public void doInvoke(String type) {BaseClass baseClass = selector.select(type);baseClass.templateMethod();}}
这里模拟的是调用端传入child1
,选择子类1进行测试,打印的结果为:
执行模板方法
执行查询方法
执行子类1的方法1
4.总结
本篇文章介绍了什么是模板方法、模板方法的简单实现与在SpringBoot中的实现的。然后对比了模板方法与策略模式的类图,发现两者天然就可以结合在一起使用。最后,通过代码实现验证了两者结合使用的可行性。
当然,本篇文章中的都是简单的示例代码,突出的只是实现的思想,在日常的开发中,可以结合实际的业务流程对上述的代码进行改造。
相关文章:

【设计模式】模板方法与策略模式的结合使用
文章目录 1. 概述1.1.简述模板方法 2.模板方法实现2.1.简单实现2.2.在SpringBoot中的实现 3.模板方法与策略模式的结合使用3.1.代码实现 4.总结 1. 概述 模板方法是一种非常简单的设计模式,只要能够理解面向对象中的继承与多态就能够理解这种设计模式,我…...

Jmeter实现参数加密
目录 一、使用__digest自带函数 以md5加密算法演示使用方法 二、在BeanShell 中使用JAVA代码实现算法加密 规避BUG的方法 JMeter有两种方法可以实现算法加密 一、使用__digest自带函数 参数说明: Digest algorithm:算法摘要,可输入值&a…...
Solon Web 开发:四、认识请求上下文(Context)
Handler Context 架构,是Solon Web 的基础。在 Context (org.noear.solon.core.handle.Context)里可以获取: 请求相关的对象与接口会话状态相关的对象与接口响应相关的对象与接口 或者理解所有请求与响应相关的,都在…...

docker安装RocketMQ(附填坑经验connect to <172.17.0.3:10909> failed)
目录 一、docker部署RocketMQ1、简易说明2、docker拉取RocketMQ镜像\RocketMQ控制台3、获取RocketMQ配置文件4、RocketMQ配置文件描述5、docker启动RocketMQ6、进入RocketMQ控制台 二、填坑经验错误一: connect to <172.17.0.3:10909> failed错误二: maybe your broker m…...

GRU、LSTM、注意力机制(第八次组会)
GRU、LSTM、注意力机制(第八次组会) 一、 GRU二、 LSTM三、 深度RNN、双向RNN四、 注意力机制一、 GRU 二、 LSTM 三、 深度RNN、双向RNN...
问题杂谈(三十六)@RequestBody、@RequestParam和@PathVariable三个注解的区别和使用
总结: 在后端的同一个接收方法里,RequestBody与RequestParam()可以同时使用RequestBody最多只能有一个,而RequestParam()可以有多个RequestBody 接收的是请求体里面的数据,所以一般用POST请求;而RequestParam接收的是…...

Flutter学习四:Flutter开发基础(六)调试Flutter应用
目录 0 引言 1 调试Flutter应用 1.1 日志与断点 1.1.1 debugger() 声明 1.1.2 print和debugPrint 1.1.3 调试模式、中间模式、发布模式 1.1.4 断点 1.2 调试应用程序层 1.2.1 转储Widgets树 1.2.2 转储渲染树 1.2.3 转储Layer树 1.2.4 转储语义树 1.2.5 调度&…...
新的开始(开始更新笔记)
首先感谢关注我的小伙伴,以后在求职或者选择方向的时候,感觉迷茫的时候,可以加我聊聊。 一路走来,跌跌撞撞,磕磕碰碰,经历了很多,记得上一次的更新笔记还是2021年。 首先说一下我的经历&#…...

爬虫工具-替换js文件ReRes插件/Gores插件
目录 一、ReRes插件二、Gores插件 一、ReRes插件 用途:爬虫逆向过程中一些文件需要替换时 ① 原始网站js文件有无限debugger,复制原始网站js文件,删掉无限debugger相关代码保存为新的js文件;用ReRes插件进行替换② 原始网站js文件…...

多任务学习用于多模态生物数据分析
目前的生物技术可以同时测量来自同一细胞的多种模态数据(例如RNA、DNA可及性和蛋白质)。这需要结合不同的分析任务(如多模态整合和跨模态分析)来全面理解这些数据,推断基因调控如何驱动生物多样性。然而,目…...

使用less命令搜索文件中的关键字
目录 介绍常用搜索技巧实例 介绍 less 与 more 类似,less 可以随意浏览文件,支持翻页和搜索,支持向上翻页和向下翻页。 语法 less [参数] 文件 参数说明: -b <缓冲区大小> 设置缓冲区的大小 -e 当文件显示结束后ÿ…...
【kubernetes系列】Kubernetes之Taints和tolerations
概述 节点亲和性是pod的一种属性(优先选择或硬性要求),它使 pod 被优先分配到一类特定的节点上。而Taint则相反,它使节点能够排斥一类特定的 pod。 Taints(污点)与tolerations(容忍度…...
宝剑锋从磨砺出 梅花香自苦寒来(高考志愿篇)
各省高考成绩已出,又到一年高考季。张雪峰提到:“普通家庭不要光谈理想,也要谈落地。”志愿怎样填报、选专业还是选学校、什么专业好就业、高考志愿主要看什么?针对这些疑问,你对正在选志愿的毕业生们有什么建议吗&…...
Jtti:怎样进行sql server2000 日志传送
在 SQL Server 2000 中,日志传送是指将事务日志从一个主服务器传送到一个或多个备份服务器的过程。这个过程确保备份服务器上的数据库保持与主服务器上的数据库同步。 要进行 SQL Server 2000 的日志传送,需要进行以下步骤: 配置主服务器&…...

MyBatis-Plus:条件构造器Wrapper
目录 1.Wrapper概述 1.1.Wrapper的继承关系 1.2.Wapper介绍 1.3.各个构造器使用区别 1.4.构造器常用方法 2.Wrapper常用构造器介绍 2.1.QueryWrapper 2.2.UpdateWrapper 2.3.LambdaQueryWrapper 2.4.AbstractWrapper 3. Lambda条件构造器 3.1.示例 4.鸣谢 MyBati…...

SNMP 计算机网络管理 实验1(二) 练习与使用Wireshark抓取SNMP数据包抓包之 任务三分析并验证TCP三次握手建立连接时三次握手工作过程
⬜⬜⬜ 🐰🟧🟨🟩🟦🟪(*^▽^*)欢迎光临 🟧🟨🟩🟦🟪🐰⬜⬜⬜ ✏️write in front✏️ 📝个人主页:陈丹宇jmu &am…...

【UE5 Cesium】03-Cesium for Unreal 添加本地数据集
上一篇:【UE5 Cesium】02-Cesium for Unreal 添加在线数据集 步骤 1. 在官网(Adding Datasets – Cesium)上下载一个示例 下载的是一个名为“Tileset.zip”的压缩文件 解压后文件内容如下 2. 打开虚幻编辑器,点击“Blank 3D Tiles…...

数通王国历险记之地址分析协议(ARP)
系列文章目录 数通王国历险记(4) 目录 前言 一,什么是地址解析协议(ARP) 二,封装和解封装 三,为什么需要地址解析协议(ARP) 四,ARP的验证实验 4.1&#x…...

Mac端显示服务器上show的内容
Mac端显示服务器上show的内容 1. 需求描述 在Mac端(终端和PyCharm中)编写代码,在服务器端运行程序。需要在Mac端显示服务器端运行的内容,比如,运行的视频等。 2. 常见报错 SSH 运行命令时报错示例。 (cv) czjing…...
【SQL】每类视频近一个月的转发量/率
【问题】 统计在有用户互动的最近一个月(按包含当天在内的近30天算,比如10月31日的近30天为10.2~10.31之间的数据)中,每类视频的转发量和转发率(保留3位小数)。 【数据】 用户-视频互动表 tb_user_video…...
rknn优化教程(二)
文章目录 1. 前述2. 三方库的封装2.1 xrepo中的库2.2 xrepo之外的库2.2.1 opencv2.2.2 rknnrt2.2.3 spdlog 3. rknn_engine库 1. 前述 OK,开始写第二篇的内容了。这篇博客主要能写一下: 如何给一些三方库按照xmake方式进行封装,供调用如何按…...

【快手拥抱开源】通过快手团队开源的 KwaiCoder-AutoThink-preview 解锁大语言模型的潜力
引言: 在人工智能快速发展的浪潮中,快手Kwaipilot团队推出的 KwaiCoder-AutoThink-preview 具有里程碑意义——这是首个公开的AutoThink大语言模型(LLM)。该模型代表着该领域的重大突破,通过独特方式融合思考与非思考…...
使用van-uploader 的UI组件,结合vue2如何实现图片上传组件的封装
以下是基于 vant-ui(适配 Vue2 版本 )实现截图中照片上传预览、删除功能,并封装成可复用组件的完整代码,包含样式和逻辑实现,可直接在 Vue2 项目中使用: 1. 封装的图片上传组件 ImageUploader.vue <te…...

CMake 从 GitHub 下载第三方库并使用
有时我们希望直接使用 GitHub 上的开源库,而不想手动下载、编译和安装。 可以利用 CMake 提供的 FetchContent 模块来实现自动下载、构建和链接第三方库。 FetchContent 命令官方文档✅ 示例代码 我们将以 fmt 这个流行的格式化库为例,演示如何: 使用 FetchContent 从 GitH…...

OPenCV CUDA模块图像处理-----对图像执行 均值漂移滤波(Mean Shift Filtering)函数meanShiftFiltering()
操作系统:ubuntu22.04 OpenCV版本:OpenCV4.9 IDE:Visual Studio Code 编程语言:C11 算法描述 在 GPU 上对图像执行 均值漂移滤波(Mean Shift Filtering),用于图像分割或平滑处理。 该函数将输入图像中的…...
Web中间件--tomcat学习
Web中间件–tomcat Java虚拟机详解 什么是JAVA虚拟机 Java虚拟机是一个抽象的计算机,它可以执行Java字节码。Java虚拟机是Java平台的一部分,Java平台由Java语言、Java API和Java虚拟机组成。Java虚拟机的主要作用是将Java字节码转换为机器代码&#x…...

Ubuntu系统多网卡多相机IP设置方法
目录 1、硬件情况 2、如何设置网卡和相机IP 2.1 万兆网卡连接交换机,交换机再连相机 2.1.1 网卡设置 2.1.2 相机设置 2.3 万兆网卡直连相机 1、硬件情况 2个网卡n个相机 电脑系统信息,系统版本:Ubuntu22.04.5 LTS;内核版本…...
第八部分:阶段项目 6:构建 React 前端应用
现在,是时候将你学到的 React 基础知识付诸实践,构建一个简单的前端应用来模拟与后端 API 的交互了。在这个阶段,你可以先使用模拟数据,或者如果你的后端 API(阶段项目 5)已经搭建好,可以直接连…...
土建施工员考试:建筑施工技术重点知识有哪些?
《管理实务》是土建施工员考试中侧重实操应用与管理能力的科目,核心考查施工组织、质量安全、进度成本等现场管理要点。以下是结合考试大纲与高频考点整理的重点内容,附学习方向和应试技巧: 一、施工组织与进度管理 核心目标: 规…...

Qt的学习(一)
1.什么是Qt Qt特指用来进行桌面应用开发(电脑上写的程序)涉及到的一套技术Qt无法开发网页前端,也不能开发移动应用。 客户端开发的重要任务:编写和用户交互的界面。一般来说和用户交互的界面,有两种典型风格&…...