策略模式Spring框架下开发实例
策略类Spring框架下开发实例
先列出策略模式下需要那些类:
策略接口 (
Strategy),定义所有策略类必须遵循的行为。具体策略类(如
ConcreteStrategyA、ConcreteStrategyB),实现不同的算法或行为。上下文类 (
Context),持有策略实例并根据条件动态选择和创建策略。辅助类和注解(作为工具辅助构建类工厂,可忽略),为策略模式的实现提供额外的功能或配置。
具体实现
定义策略接口
采用策略模式的情况一般是封装多重if-else/switch下的不同处理逻辑,这些逻辑可以抽象为一个行为(比如封装参数,做前置校验等),将这个行为定义为接口方法
/*** 策略接口*/
public interface ProductTypeHandler {//本例中根据不同产品类型执行不同的返回参数封装逻辑void assemble(ResponseBO resBO);
}
定义策略类及其处理逻辑
将上文提到的多重if-else/switch中不同情况及其处理逻辑抽象成具体策略类和其内部方法执行逻辑
/*** 记录器参数处理类*/
@Component
//注解使用见3
@ProductTypeAssembleTag(TAG = EnumProductType.PROCESS_RECORDER)
public class RecorderHandler implements ProductTypeHandler {@Autowiredprivate RecorderService service;@Overridepublic void assemble(ResponseBO resBO) {//记录器的处理逻辑int bindDeviceNums = service.getNumByUserId(ContextUtil.getUserId());resBO.setValue(bindDeviceNums);}
}
@Component
@ProductTypeAssembleTag(TAG = EnumProductType.COMMANDER)
//继承见4
public class CommanderHandler extends CommonHandler implements ProductTypeHandler {@Overridepublic void assemble(ResponseBO resBO) {//控制器的处理逻辑...}
}
这里用到的注解和继承可以先忽视,3,4中会做解释
Context上下文类
这个类的核心就是存储
map所有的策略类,然后提供getHandler()方法,供外界匹配获取对应策略类执行相关逻辑那么这个map是如何存储的呢?我在实践中遇到是通过
Map<TypeEnum,Handler>也就是定义枚举类作为键,存储其对应枚举类此处有一个可拓展的点就是这个map是如何封装的,我遇到的有这么两种:
- 在Context中将所有策略类定义为属性,然后@Resourse注入
- 通过注解的方式进行解耦
第一种自动注入+switch的方式
@Component
public class ProductTypeHandlerContext {@Resourceprivate CommonHandler commonHandler;@ResourceprivateRecorderHandler recorderHandler;//...其他策略类public ProductTypeHandler getHandler(EnumProductType emumType) {ProductTypeHandler handler = null;switch (EnumProductType) {case COMMANDER:handler = commonHandler;break;case RECORDER:handler = recorderHandler;break;default:throw new BizException("未匹配到目标类型");break;}return handler;}
}
可以看到第一种方案比较轻量级,下面来着重举例看看第二种情况:
@Component
public class HandlerContext{private static final Map<EnumProductType, ProductTypeHandler> handlerMap = new HashMap<>();@Autowiredprivate HandlerContext(List<ProductTypeHandler> handlers) {handlers.forEach(processor -> {//这一段是通过反射获取到策略类long count = new ArrayList<>(Arrays.asList(processor.getClass().getInterfaces())).stream().filter(m -> m.getName().equals("org.springframework.aop.framework.Advised")).count();try {Class<?> ifaceClass = (Class) (count > 0 ? processor.getClass().getMethod("getTargetClass").invoke(processor) : processor.getClass());this.handlerMap.put(ifaceClass.getAnnotation(ProductTypeAssembleTag.class).TAG(), processor);} catch (Exception e) {log.error("HandlerContext初始化失败",e);throw new BizException("HandlerContext初始化失败");}});}public ProductTypeHandler getHandler(EnumProductType emumType) {return handlerMap.get(emumType);}
}
可以看到Context主要分为三部分:
- map
- 构造函数–>初始化map
- getHandler()
这里的重点在2中的HandlerContext()构造方法,通过@Autowired注解,Spring会将所有继承了策略接口ProductTypeHandler的策略类注入为列表作为参数,那么在方法内部对列表中的策略类依次进行map.put就可以了
那如何将策略类对应到其所属的EnumType上呢?这里通过自定义一个注解,为其添加一个名为Tag的EnumType属性,在每个策略类上面加上这个注解,就大功告成了,这也是上面的策略类的注解来源
/*** 注解*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface ProductTypeAssembleTag {EnumsLogin.EnumProductType TAG();
}
至于HandlerContext()中的try中所涉及的逻辑,是检查当前的 processor 类是否是一个 Spring AOP 代理类:
"org.springframework.aop.framework.Advised"接口是 Spring AOP 代理类的标志。如果 count > 0,说明 processor 是一个 AOP 代理类。
如果 processor 是 AOP 代理类,则调用 processor.getClass().getMethod("getTargetClass").invoke(processor) 获取目标类的 Class 对象,这样可以获取到代理类的实际目标类(即被代理的类)。如果 processor 不是代理类,则直接使用 processor.getClass()。
getTargetClass()方法是 AOP 代理类的一个方法,它返回代理对象的实际目标类(即未被代理的原始类)。
进一步思考
如果策略类a,b的执行逻辑是一样的,那么要写重复的代码吗?
这时候就可以回归到最基础的继承上,通过定义一个CommonHandler,在其中写一次默认逻辑,然后其他策略类进行继承这也是上面的策略类有继承的原因
public class CommonHandler {public void assemble(ResponseBO resBO) {log.info("默认处理--目前没有特殊处理逻辑");}
}
至此,一个通过注解和继承优化的策略类实现方案就得以使用,在调用类通过注入HandlerContext,调用getHandler(enumType).assemble就可以传入枚举类获取对应策略类执行对应逻辑
总结
针对Context中map的两种初始化方案:
| 方案 | 优点 | 缺点 |
|---|---|---|
| 自动注入+switch | - 简单直观,易于理解和维护 - 性能较好,避免了反射开销 | - 扩展性差,需要修改 switch 语句才能添加新策略- 高耦合,修改策略类需要修改 Context 类 |
| 注解解耦 | - 解耦性强,符合开闭原则 - 易于扩展,不需要修改现有代码 - 支持 AOP 代理 | - 性能开销较高,使用了反射 – 启动时的反射性能开销 |
相关文章:
策略模式Spring框架下开发实例
策略类Spring框架下开发实例 先列出策略模式下需要那些类: 策略接口 (Strategy),定义所有策略类必须遵循的行为。 具体策略类(如 ConcreteStrategyA、ConcreteStrategyB),实现不同的算法或行为。 上下文类 (Context),…...
量子计算驱动的金融衍生品定价革命:突破传统蒙特卡洛模拟的性能边界
引言:金融计算的算力困局 某国际投行采用128量子位处理器对亚洲期权组合定价时,其量子振幅估计算法在2.7秒内完成传统GPU集群需要68小时的计算任务。在蒙特卡洛路径模拟实验中,量子随机游走算法将10,000维衍生品的价格收敛速度提升4个数量级…...
华为昇腾 910B 部署 DeepSeek-R1 蒸馏系列模型详细指南
本文记录 在 华为昇腾 910B(65GB) * 8 上 部署 DeepSeekR1 蒸馏系列模型(14B、32B)全过程与测试结果。 NPU:910B3 (65GB) * 8 (910B 有三个版本 910B1、2、3) 模型:DeepSeek-R1-Distill-Qwen-14B、DeepSeek…...
pyscenic运行报错:ValueError: Intersection of gene_names and tf_names is empty
pyscenic运行报错:ValueError: Intersection of gene_names and tf_names is empty 首先查一下是否有重复基因 python check_common_genes.pycheck_common_genes.py import pandas as pd# 定义文件路径 #这是转置后的基因表达矩阵 expression_matrix_file "…...
Selenium实战案例2:东方财富网股吧评论爬取
上一篇文章,我们使用Selenium完成了网页内文件的自动下载,本文我们将使用Selenium来爬取东方财富网股吧内笔记的评论数据。 网页内容分析 网页内容的分析是web自动化中的关键一步。通过分析网页结构,我们可以确定需要抓取的数据位置以及操作元素的方式。…...
python的多线程机制和构造
Python的多线程机制和构造是一个复杂且多方面的主题,涉及到线程的基本概念、实现方式、同步机制以及实际应用。以下将详细介绍Python中的多线程机制和构造。 1. 线程的基本概念 线程是进程内的执行单元,每个线程共享进程的地址空间和资源。一个进程至少…...
webmin配置终端显示样式,模仿UbuntuDesktop终端
webmin配置终端显示样式,模仿UbuntuDesktop终端 在webmin中,默认情况下是没有图形化桌面的,因此终端界面也不会像 Ubuntu Desktop 那样有预设的紫色背景和颜色主题。不过,你可以通过修改 ~/.bashrc 文件,并结合安装和…...
移动通信发展史
概念解释 第一代网络通信 1G 第二代网络通信 2G 第三代网络通信 3G 第四代网络通信 4G 4g网络有很高的速率和很低的延时——高到500M的上传和1G的下载 日常中的4G只是用到了4G技术 运营商 移动-从民企到国企 联通-南方教育口有人 电信 铁通:成立于 2000 年…...
OutOfMemoryError unable to create new native thread
现象 生产环境大量的报OutOfMemoryError: unable to create new native thread Caused by: java.lang.OutOfMemoryError: unable to create new native threadat java.lang.Thread.start0(Native Method) [na:1.8.0_291]at java.lang.Thread.start(Thread.java:717) [na:1.8.…...
探索无网用Deepseek+qwen来助力Solidworks二次开发
在本教程中,我们将详细介绍如何在本地环境中使用 DeepSeek 和 Qwen 模型,结合 AnythingLLM,构建一个用于 SolidWorks 二次开发的私有化智能知识库。 目录 前言 环境准备 2.1 安装 Ollama 2.2 安装 Docker Desktop DeepSeek 本地部署 3.1…...
MAC快速本地部署Deepseek (win也可以)
MAC快速本地部署Deepseek (win也可以) 下载安装ollama 地址: https://ollama.com/ Ollama 是一个开源的大型语言模型(LLM)本地运行框架,旨在简化大模型的部署和管理流程,使开发者、研究人员及爱好者能够高效地在本地环境中实验和…...
deepseek清华大学第二版 如何获取 DeepSeek如何赋能职场应用 PDF文档 电子档(附下载)
deepseek清华大学第二版 DeepSeek如何赋能职场 pdf文件完整版下载 https://pan.baidu.com/s/1aQcNS8UleMldcoH0Jc6C6A?pwd1234 提取码: 1234 或 https://pan.quark.cn/s/3ee62050a2ac...
ResponseUtil.out 方法分析
文章目录 1. 问题背景2. ResponseUtil.out 方法分析a. 方法功能b. 序列化过程c. 注解 JsonInclude(JsonInclude.Include.NON_NULL) 的作用 3. Java 对象如何被序列化为 JSON4. 序列化的时机5. 谁操作序列化6. 自动序列化的条件7. 总结8. 可能的问题和注意 1. 问题背景 在 Admi…...
基于Flask框架的食谱数据可视化分析系统的设计与实现
【Flask】基于Flask框架的食谱数据可视化分析系统的设计与实现 (完整系统源码开发笔记详细部署教程)✅ 目录 一、项目简介二、项目界面展示三、项目视频展示 一、项目简介 在当今数字化时代,信息可视化已成为一种高效的数据理解和传播手段。…...
【量化策略】布林带突破策略
【量化策略】布林带突破策略 🚀量化软件开通 🚀量化实战教程 技术背景与应用场景 布林带(Bollinger Bands)是由约翰布林格(John Bollinger)在1980年代初期开发的一种技术分析工具,它通过计算…...
java后端开发day18--学生管理系统
(以下内容全部来自上述课程) 1.业务分析并搭建主菜单 1.需求 采取控制台的方式去书写学生管理系统 2.分析 1.初始菜单 2.学生类 属性:id,姓名,年龄,家庭住址 3.添加功能 键盘录入每一个学生信息并添…...
工厂车辆排队系统
工厂车辆排队系统是一种智能化调度管理系统,用于管理工厂内部所有车辆的进出和排队方式。采用JAVA语言开发,对接了仰邦控制卡硬件。 工厂车辆排队系统是一种智能化调度管理系统,用于管理工厂内部所有车辆的进出和排队方式。该系统可以提高车…...
深度理解多态的底层实现
前言 首先先回顾一下上次的知识 一、多态的概念 多态(polymorphism)的概念:通俗来说,就是多种形态。多态分为编译时多态(静态多态)和运⾏时多态(动态多态),这⾥我们重点讲运⾏时多态,编译时多态(静态多态)和运⾏时多态(动态多态…...
【深度学习】Pytorch项目实战-基于协同过滤实现物品推荐系统
一、推荐系统的了解 1. 定义 推荐系统是一个信息过滤系统,旨在为用户提供个性化的内容推荐。它利用用户的历史行为、偏好以及其他相关数据来推测用户可能感兴趣的项目或信息。推荐系统广泛应用于电子商务、社交媒体、流媒体服务等领域,帮助用户发现商品…...
空字符串““、空白字符串“ “和 null 三者的区别
空字符串、空白字符串和 null 三者的区别表格: 类型定义示例长度是否有值空字符串字符串长度为 0,但不是 null,即存在一个有效的空字符串对象。""0有值(空值)空白字符串字符串包含空格、制表符等空白字符&a…...
【Pandas】pandas Series sample
Pandas2.2 Series Computations descriptive stats 方法描述Series.align(other[, join, axis, level, …])用于将两个 Series 对齐,使其具有相同的索引Series.case_when(caselist)用于根据条件列表对 Series 中的元素进行条件判断并返回相应的值Series.drop([lab…...
AF3 _build_query_to_hit_index_mapping函数解读
AlphaFold3 中templates模块的_build_query_to_hit_index_mapping函数是将原始查询序列(original_query_sequence)中的索引与hit 序列(hit_sequence)中的索引进行映射。 在蛋白质序列比对(如 HHsearch)中,hit 是与查询序列部分匹配的区域。由于存在缺口(-)和部分比对…...
在mfc中使用自定义三维向量类和计算多个三维向量的平均值
先添加一个普通类, Vector3.h, // Vector3.h: interface for the Vector3 class. // //#if !defined(AFX_VECTOR3_H__53D34D26_95FF_4377_BD54_57F4271918A4__INCLUDED_) #define AFX_VECTOR3_H__53D34D26_95FF_4377_BD54_57F4271918A4__INCLUDED_#if _MSC_VER > 1000 #p…...
UE_C++ —— Container TSet
目录 一,TSet 二,Creating and Filling a Set Editing UPROPERTY TSets 三,Iteration 四,Queries 五,Removal 六,Sorting 七,Operators 八,Slack 九,DefaultKe…...
多线程和并发篇
多线程和并发篇 创建一个对象时底层汇编指令实现步骤(cpu可能会进行指令重排序):一、二、三级缓存的实现:并发编程三要素:线程的五大状态:创建线程的三种方式:线程的特征和状态:Thre…...
【3.5JavaScript】JavaScript字符串对象
文章目录 1.获取字符串长度2.大小写转换3.获取某一个字符4.截取字符串5.替换字符串6.分割字符串7.检索字符串位置8.例题:统计某一个字符的个数 在 JavaScript 中,对象是非常重要的知识点。对象分为两种:一种是 ”自定义对象“,另…...
Apache Flink架构深度解析:任务调度、算子数据同步与TaskSlot资源管理机制
Apache Flink是一个分布式流处理框架,其核心架构设计围绕有界与无界数据流的统一处理能力展开。以下从任务分配、算子数据同步、TaskManager与JobManager的TaskSlot机制三个维度展开详细分析: 一、任务分配机制 Flink的任务分配基于并行度(P…...
路由基本配置
学习目标 • 根据拓扑图进行网络布线。 • 清除启动配置并将路由器重新加载为默认状态。 • 在路由器上执行基本配置任务。 • 配置并激活以太网接口。 • 测试并检验配置。 • 思考网络实施方案并整理成文档。 任务 1:网络布线 使用适当的电缆类型连接网络设备。…...
windows上vscode cmake工程搭建
安装vscode插件: 1.按装fastc(主要是安装MinGW\mingw64比较方便) 2.安装C,cmake,cmake tools插件 3.准备工作完成之后,按F1,选择cmake:Quick Start就可以创建一个cmake工程。 4.设置Cmake: G…...
VUE3+TS+element-plus项目从0开始入门 - 创建项目、认识基本结构
文章目录 写在前面1、创建vue3项目npm create vuelatestnpm i 2、项目结构.vscodevue3结构a、项目树结构b、package.jsonc、tsconfig.jsond、index.htmld、srce、main.tsf、App.vue 写在前面 开前请自行下载vs code、node.js, 在vs code里面安装Vue - Official插件。本文使用的…...
