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

【设计模式】模板方法与策略模式的结合使用

文章目录

  • 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. 概述 模板方法是一种非常简单的设计模式&#xff0c;只要能够理解面向对象中的继承与多态就能够理解这种设计模式&#xff0c;我…...

Jmeter实现参数加密

目录 一、使用__digest自带函数 以md5加密算法演示使用方法 二、在BeanShell 中使用JAVA代码实现算法加密 规避BUG的方法 JMeter有两种方法可以实现算法加密 一、使用__digest自带函数 参数说明&#xff1a; Digest algorithm&#xff1a;算法摘要&#xff0c;可输入值&a…...

Solon Web 开发:四、认识请求上下文(Context)

Handler Context 架构&#xff0c;是Solon Web 的基础。在 Context &#xff08;org.noear.solon.core.handle.Context&#xff09;里可以获取&#xff1a; 请求相关的对象与接口会话状态相关的对象与接口响应相关的对象与接口 或者理解所有请求与响应相关的&#xff0c;都在…...

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三个注解的区别和使用

总结&#xff1a; 在后端的同一个接收方法里&#xff0c;RequestBody与RequestParam()可以同时使用RequestBody最多只能有一个&#xff0c;而RequestParam()可以有多个RequestBody 接收的是请求体里面的数据&#xff0c;所以一般用POST请求&#xff1b;而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 调度&…...

新的开始(开始更新笔记)

首先感谢关注我的小伙伴&#xff0c;以后在求职或者选择方向的时候&#xff0c;感觉迷茫的时候&#xff0c;可以加我聊聊。 一路走来&#xff0c;跌跌撞撞&#xff0c;磕磕碰碰&#xff0c;经历了很多&#xff0c;记得上一次的更新笔记还是2021年。 首先说一下我的经历&#…...

爬虫工具-替换js文件ReRes插件/Gores插件

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

多任务学习用于多模态生物数据分析

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

使用less命令搜索文件中的关键字

目录 介绍常用搜索技巧实例 介绍 less 与 more 类似&#xff0c;less 可以随意浏览文件&#xff0c;支持翻页和搜索&#xff0c;支持向上翻页和向下翻页。 语法 less [参数] 文件 参数说明&#xff1a; -b <缓冲区大小> 设置缓冲区的大小 -e 当文件显示结束后&#xff…...

【kubernetes系列】Kubernetes之Taints和tolerations

概述 节点亲和性是pod的一种属性&#xff08;优先选择或硬性要求&#xff09;&#xff0c;它使 pod 被优先分配到一类特定的节点上。而Taint则相反&#xff0c;它使节点能够排斥一类特定的 pod。 Taints&#xff08;污点&#xff09;与tolerations&#xff08;容忍度&#xf…...

宝剑锋从磨砺出 梅花香自苦寒来(高考志愿篇)

各省高考成绩已出&#xff0c;又到一年高考季。张雪峰提到&#xff1a;“普通家庭不要光谈理想&#xff0c;也要谈落地。”志愿怎样填报、选专业还是选学校、什么专业好就业、高考志愿主要看什么&#xff1f;针对这些疑问&#xff0c;你对正在选志愿的毕业生们有什么建议吗&…...

Jtti:怎样进行sql server2000 日志传送

在 SQL Server 2000 中&#xff0c;日志传送是指将事务日志从一个主服务器传送到一个或多个备份服务器的过程。这个过程确保备份服务器上的数据库保持与主服务器上的数据库同步。 要进行 SQL Server 2000 的日志传送&#xff0c;需要进行以下步骤&#xff1a; 配置主服务器&…...

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三次握手建立连接时三次握手工作过程

⬜⬜⬜ &#x1f430;&#x1f7e7;&#x1f7e8;&#x1f7e9;&#x1f7e6;&#x1f7ea;(*^▽^*)欢迎光临 &#x1f7e7;&#x1f7e8;&#x1f7e9;&#x1f7e6;&#x1f7ea;&#x1f430;⬜⬜⬜ ✏️write in front✏️ &#x1f4dd;个人主页&#xff1a;陈丹宇jmu &am…...

【UE5 Cesium】03-Cesium for Unreal 添加本地数据集

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

数通王国历险记之地址分析协议(ARP)

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

Mac端显示服务器上show的内容

Mac端显示服务器上show的内容 1. 需求描述 在Mac端&#xff08;终端和PyCharm中&#xff09;编写代码&#xff0c;在服务器端运行程序。需要在Mac端显示服务器端运行的内容&#xff0c;比如&#xff0c;运行的视频等。 2. 常见报错 SSH 运行命令时报错示例。 (cv) czjing…...

【SQL】每类视频近一个月的转发量/率

【问题】 统计在有用户互动的最近一个月&#xff08;按包含当天在内的近30天算&#xff0c;比如10月31日的近30天为10.2~10.31之间的数据&#xff09;中&#xff0c;每类视频的转发量和转发率&#xff08;保留3位小数&#xff09;。 【数据】 用户-视频互动表 tb_user_video…...

MPNet:旋转机械轻量化故障诊断模型详解python代码复现

目录 一、问题背景与挑战 二、MPNet核心架构 2.1 多分支特征融合模块(MBFM) 2.2 残差注意力金字塔模块(RAPM) 2.2.1 空间金字塔注意力(SPA) 2.2.2 金字塔残差块(PRBlock) 2.3 分类器设计 三、关键技术突破 3.1 多尺度特征融合 3.2 轻量化设计策略 3.3 抗噪声…...

Vue记事本应用实现教程

文章目录 1. 项目介绍2. 开发环境准备3. 设计应用界面4. 创建Vue实例和数据模型5. 实现记事本功能5.1 添加新记事项5.2 删除记事项5.3 清空所有记事 6. 添加样式7. 功能扩展&#xff1a;显示创建时间8. 功能扩展&#xff1a;记事项搜索9. 完整代码10. Vue知识点解析10.1 数据绑…...

java 实现excel文件转pdf | 无水印 | 无限制

文章目录 目录 文章目录 前言 1.项目远程仓库配置 2.pom文件引入相关依赖 3.代码破解 二、Excel转PDF 1.代码实现 2.Aspose.License.xml 授权文件 总结 前言 java处理excel转pdf一直没找到什么好用的免费jar包工具,自己手写的难度,恐怕高级程序员花费一年的事件,也…...

全球首个30米分辨率湿地数据集(2000—2022)

数据简介 今天我们分享的数据是全球30米分辨率湿地数据集&#xff0c;包含8种湿地亚类&#xff0c;该数据以0.5X0.5的瓦片存储&#xff0c;我们整理了所有属于中国的瓦片名称与其对应省份&#xff0c;方便大家研究使用。 该数据集作为全球首个30米分辨率、覆盖2000–2022年时间…...

在 Nginx Stream 层“改写”MQTT ngx_stream_mqtt_filter_module

1、为什么要修改 CONNECT 报文&#xff1f; 多租户隔离&#xff1a;自动为接入设备追加租户前缀&#xff0c;后端按 ClientID 拆分队列。零代码鉴权&#xff1a;将入站用户名替换为 OAuth Access-Token&#xff0c;后端 Broker 统一校验。灰度发布&#xff1a;根据 IP/地理位写…...

渲染学进阶内容——模型

最近在写模组的时候发现渲染器里面离不开模型的定义,在渲染的第二篇文章中简单的讲解了一下关于模型部分的内容,其实不管是方块还是方块实体,都离不开模型的内容 🧱 一、CubeListBuilder 功能解析 CubeListBuilder 是 Minecraft Java 版模型系统的核心构建器,用于动态创…...

从零开始打造 OpenSTLinux 6.6 Yocto 系统(基于STM32CubeMX)(九)

设备树移植 和uboot设备树修改的内容同步到kernel将设备树stm32mp157d-stm32mp157daa1-mx.dts复制到内核源码目录下 源码修改及编译 修改arch/arm/boot/dts/st/Makefile&#xff0c;新增设备树编译 stm32mp157f-ev1-m4-examples.dtb \stm32mp157d-stm32mp157daa1-mx.dtb修改…...

GC1808高性能24位立体声音频ADC芯片解析

1. 芯片概述 GC1808是一款24位立体声音频模数转换器&#xff08;ADC&#xff09;&#xff0c;支持8kHz~96kHz采样率&#xff0c;集成Δ-Σ调制器、数字抗混叠滤波器和高通滤波器&#xff0c;适用于高保真音频采集场景。 2. 核心特性 高精度&#xff1a;24位分辨率&#xff0c…...

Java线上CPU飙高问题排查全指南

一、引言 在Java应用的线上运行环境中&#xff0c;CPU飙高是一个常见且棘手的性能问题。当系统出现CPU飙高时&#xff0c;通常会导致应用响应缓慢&#xff0c;甚至服务不可用&#xff0c;严重影响用户体验和业务运行。因此&#xff0c;掌握一套科学有效的CPU飙高问题排查方法&…...

C++.OpenGL (14/64)多光源(Multiple Lights)

多光源(Multiple Lights) 多光源渲染技术概览 #mermaid-svg-3L5e5gGn76TNh7Lq {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-3L5e5gGn76TNh7Lq .error-icon{fill:#552222;}#mermaid-svg-3L5e5gGn76TNh7Lq .erro…...