策略模式在工作中的运用
前言
在不同的场景下,执行不同的业务逻辑,在日常工作中是很寻常的事情。比如,订阅系统。在收到阿里云的回调事件、与收到AWS的回调事件,无论是收到的参数,还是执行的逻辑都可能是不同的。为了避免,每次新增一种场景,就要改变原有的代码结构,比如,改变原有逻辑,添加 if-else结构,一种可行的方案是,使用策略模式。
策略模式
贴个链接
简单来说,就是根据请求方的类型,执行特定的业务逻辑。
问题点
网络上大部分实现策略模式的代码,很多在将具体的策略注入到Map中时,是以硬编码的方式实现的,比如掘金上的这篇文章:如何优雅的将设计模式运用到实际项目中去?
在工作中借鉴,使用截图:
这种方案,虽然可以实现策略模式,但是每次新增策略都要修改这个集合。期望的方案是,新增的策略,自动注入到map中,而不必手动添加。
为了解决新增策略时,要修改map的情况,调研之后,发现有两种方案:
- 美团文章推介的 基于单例的方式,每次启动时,自动将策略注入到map中。
- 掘金上另一种方案:基于注解+反射的方式,动态将策略加载到map中。
相比,美团的方案更加优秀,代码改动少,且性能高。不过,本次以方案2为例说明。
代码实现
业务场景
需要提供给外部云厂商回调API,当云服务发生告警时,调用此API,将告警事情同步到服务使用方。
考虑点:
- 因为每家厂商的回调参数各有不同,此时,需要定义一个回调对象基类,每个云厂商对应的回调对象继承这个基类,并实现添加其特定的属性。
- 因为API是放开到公网上的,因此,为了避免被攻击,除了从集团域名出去外,还对IP进行了频控,比如,调用次数100次/秒。
代码结构
具体代码
定义实体类
基类
import lombok.Data;/*** @author wangbin16* @date 2024/1/18 15:33*/
@Data
public class CloudAlertBase {/*** name = "alertType", value = "告警类型"*/private Integer alertType;/*** 业务*/private Integer business;
}
阿里云
import lombok.Data;/*** 阿里云监控告警* @author wangbin16* @date 2024/1/18 15:35*/
@Data
public class AliyunCloudAlertAo extends CloudAlertBase {private static final long serialVersionUID = 1L;/*** name = "alertName", value = "报警名称"*/private String alertName;/*** name = "alertState", value = "报警状态"*/private String alertState;/*** name = "curValue", value = "报警发生或恢复时监控项的当前值"*/private String curValue;/*** name = "dimensions", value = "发生报警的对象"*/private String dimensions;/*** name = "expression", value = "报警规则的表达式"*/private String expression;
策略模式
策略基类
/*** @author wangbin16* @date 2024/1/18 15:41*/
public interface CloudAlertStrategy {/*** 处理云监控告警* @param param*/void handleCloudAlert(CloudAlertBase param);
}
阿里云策略
@Slf4j
@Service
@CloudAlertAnnotation(alertType = CloudAlertTypeEnum.ALIYUN)
public class AliyunCloudAlertStrategy implements CloudAlertStrategy {@Overridepublic void handleCloudAlert(CloudAlertBase param) {// TODO 处理阿里云监控告警// 暂时先发送给我 @wangbin16SendMsgUtils sendMsgUtils = new SendMsgUtils();sendMsgUtils.sendP2pPopoMsg(JSON.toJSONString(param), "wangbin16@xxx", "【阿里云监控告警】");}
}
策略注册
import org.reflections.Reflections;import java.util.HashMap;
import java.util.Map;
import java.util.Set;/*** @author wangbin16* @date 2024/1/18 15:50*/
public class AnnotationCloudAlertStrategyFactory {/*** 存储策略*/static Map<Integer, CloudAlertStrategy> strategyMap = new HashMap<>();static {registerStrategy();}/*** 自动注册策略*/private static void registerStrategy() {// 通过反射获取所有的策略类Reflections reflections = new Reflections(CloudAlertStrategy.class.getPackage().getName());Set<Class<? extends CloudAlertStrategy>> cloudStrategyClassSet = reflections.getSubTypesOf(CloudAlertStrategy.class);if (cloudStrategyClassSet != null) {for (Class<?> clazz : cloudStrategyClassSet) {// 找到类型注解,自动完成策略注册if (clazz.isAnnotationPresent(CloudAlertAnnotation.class)) {CloudAlertAnnotation alertTypeAnnotation = clazz.getAnnotation(CloudAlertAnnotation.class);CloudAlertTypeEnum chargeType = alertTypeAnnotation.alertType();try {strategyMap.put(chargeType.getCode(), (CloudAlertStrategy) clazz.newInstance());} catch (InstantiationException | IllegalAccessException e) {e.getStackTrace();}}}}}/*** 提供注册策略接口,外部只需要调用此接口接口新增策略* 策略定义时,即注入,这是口子*/public static void registerChargeStrategy(CloudAlertTypeEnum alertType, CloudAlertStrategy strategy) {strategyMap.put(alertType.getCode(), strategy);}
}
策略选择器
@Service
@Slf4j
public class CloudAlertStrategySelector {public CloudAlertStrategy selector(Integer alertType) {if (alertType == null) {return null;}return AnnotationCloudAlertStrategyFactory.strategyMap.get(alertType);}
}
服务调用
@Slf4j
@Service("cloudMonitorService")
public class CloudMonitorService {@Autowiredprivate CloudAlertStrategySelector cloudAlertStrategySelector;public void handleCloudMonitor(CloudAlertBase param) {logger.info("handleCloudMonitor param:{}", JSON.toJSONString(param));CloudAlertStrategy selector = cloudAlertStrategySelector.selector(param.getAlertType());if (selector != null) {selector.handleCloudAlert(param);} else {logger.error("外部云告警异常调用, param:{}", JSON.toJSONString(param));}}
}
测试
调用指定的接口,执行指定的逻辑
相关文章:

策略模式在工作中的运用
前言 在不同的场景下,执行不同的业务逻辑,在日常工作中是很寻常的事情。比如,订阅系统。在收到阿里云的回调事件、与收到AWS的回调事件,无论是收到的参数,还是执行的逻辑都可能是不同的。为了避免,每次新增…...

【go】依赖倒置demo
文章目录 前言1 项目目录结构:2 初始化函数3 router4 api5 service6 dao7 Reference 前言 为降低代码耦合性,采用依赖注入的设计模式。原始请求路径:router -> api -> service -> dao。请求的为实际方法,具有层层依赖的…...

C++ //练习 2.5 指出下述字面值的数据类型并说明每一组内几种字面值的区别:
C Primer(第5版) 练习 2.5 练习 2.5 指出下述字面值的数据类型并说明每一组内几种字面值的区别: ( a ) ‘a’, L’a’, “a”, L"a" ( b ) 10, 10u, 10L, 10uL, 012, 0xC ( c ) 3.14, 3.14f, 3.14L ( d ) 10, 10u, 10., 10e-2…...

必示科技助力中国联通智网创新中心通过智能化运维(AIOps)通用能力成熟度3级评估
2023年12月15日,中国信息通信研究院隆重公布了智能化运维AIOps系列标准最新批次评估结果。 必示科技与中国联通智网创新中心合作的“智能IT故障监控定位分析能力建设项目”通过了中国信息通信研究院开展的《智能化运维能力成熟度系列标准 第1部分:通用能…...

python数字图像处理基础(九)——特征匹配
目录 蛮力匹配(ORB匹配)RANSAC算法全景图像拼接 蛮力匹配(ORB匹配) Brute-Force匹配非常简单,首先在第一幅图像中选取一个关键点然后依次与第二幅图像的每个关键点进行(描述符)距离测试&#x…...

k8s的对外服务ingress
1、service的作用体现在两个方面 (1)集群内部:不断跟踪pod的变化,更新deployment中的pod对象,基于pod的ip地址不断变化的一种服务发现机制 (2)集群外部:类似于负载均衡器ÿ…...

[足式机器人]Part2 Dr. CAN学习笔记- Kalman Filter卡尔曼滤波器Ch05-3+4
本文仅供学习使用 本文参考: B站:DR_CAN Dr. CAN学习笔记 - Kalman Filter卡尔曼滤波器 Ch05-34 3. Step by step : Deriation of Kalmen Gain 卡尔曼增益/因数 详细推导4. Priori/Posterrori error Covariance Martix 误差协方差矩阵 3. Step by step :…...

关于前端面试中forEach方法的灵魂7问?
目录 前言 一、forEach方法支持处理异步函数吗? 二、forEach方法在循环过程中能中断吗? 三、forEach 在删除自己的元素后能重置索引吗? 四、forEach 的性能相比for循环哪个好? 五、使用 forEach 会不会改变原来的数组&#…...

AI小程序添加深度合成类目解决办法
基于文言一心和gpt等大模型做了一个ai助理小程序,在提交“一点AI助理”小程序时,审核如下: 失败原因1 审核失败原因 你好,你的小程序涉及提供提供文本深度合成技术 (如: AI问答) 等相关服务,请补充选择:深度…...

C/C++ BM6判断链表中是否有环
文章目录 前言题目解决方案一1.1 思路阐述1.2 源码 解决方案二2.1 思路阐述2.2 源码 总结 前言 做了一堆单链表单指针的题目,这次是个双指针题,这里双指针的作用非常明显。 题目 判断给定的链表中是否有环。如果有环则返回true,否则返回fal…...

【Java 设计模式】结构型之适配器模式
文章目录 1. 定义2. 应用场景3. 代码实现结语 适配器模式(Adapter Pattern)是一种结构型设计模式,用于将一个类的接口转换成客户端期望的另一个接口。这种模式使得原本由于接口不兼容而不能一起工作的类可以一起工作。在本文中,我…...

使用函数计算,数禾如何实现高效的数据处理?
作者:邱鑫鑫,王彬,牟柏旭 公司背景和业务 数禾科技以大数据和技术为驱动,为金融机构提供高效的智能零售金融解决方案,服务银行、信托、消费金融公司、保险、小贷公司等持牌金融机构,业务涵盖消费信贷、小…...

卷积和滤波对图像操作的区别
目录 问题引入 解释 卷积 滤波 问题引入 卷积和滤波是很相似的,都是利用了卷积核进行操作 那么他们之间有什么区别呢? 卷积:会影响原图大小 滤波:不会影响原图大小 解释 卷积 我们用这样一段代码来看 import torch.nn as …...

李沐深度学习-线性回归从零开始
# 核心Tensor,autograd import torch from IPython import display import numpy as np import random from matplotlib import pyplot as pltimport syssys.path.append(路径) from d2lzh_pytorch import * backward()函数:一次小批量执行完在进行反向传播 线性回归…...

CentOS 8.5 安装图解
特特特别的说明 CentOS发行版已经不再适合应用于生产环境,客观条件不得不用的话,优选7.9版本,8.5版本次之,最次6.10版本(比如说Oracle 11GR2就建议在6版本上部署)! 引导和开始安装 选择倒计时结…...

好用的流程图工具
分享工作中常用的装逼工具 目前市面上的流程图或者思维导图工具挺多的,但是有的会限制使用数量或者收费,典型的有processon、Xmind,推荐今天Mermaid(官网)。 快速上手 中文教程:Mermaid 初学者用户指南 | Mermaid 中文网。我们选择…...

数据结构:链式栈
stack.h /* * 文件名称:stack.h * 创 建 者:cxy * 创建日期:2024年01月18日 * 描 述: */ #ifndef _STACK_H #define _STACK_H#include <stdio.h> #include <stdlib.h>typedef struct stack{int data…...

openssl3.2 - 官方demo学习 - mac - gmac.c
文章目录 openssl3.2 - 官方demo学习 - mac - gmac.c概述笔记END openssl3.2 - 官方demo学习 - mac - gmac.c 概述 使用GMAC算法, 设置参数(指定加密算法 e.g. AES-128-GCM, 设置iv) 用key执行初始化, 然后对明文生成MAC数据 官方注释给出建议, key, iv最好不要硬编码出现在程…...

HugggingFace 推理 API、推理端点和推理空间相关模型部署和使用以及介绍
HugggingFace 推理 API、推理端点和推理空间相关模型部署和使用以及介绍。 Hugging Face是一家开源模型库公司。 2023年5月10日,Hugging Face宣布C轮1亿美元融资,由Lux Capital领投,红杉资本、Coatue、Betaworks、NBA球星Kevin Durant等跟投…...

python的tabulate包在命令行下输出表格不对齐
用tabulate可以在命令行下输出表格。 from tabulate import tabulate# 定义表头 headers [列1, 列2, 列3]# 每行的内容 rows [] rows.append((张三,数学,英语)) rows.append((李四,信息科技,数学))# 使用 tabulate 函数生成表格 output tabulate(rows, headersheaders, tab…...

LLM之幻觉(二):大语言模型LLM幻觉缓减技术综述
LLM幻觉缓减技术分为两大主流,梯度方法和非梯度方法。梯度方法是指对基本LLM进行微调;而非梯度方法主要是在推理时使用Prompt工程技术。LLM幻觉缓减技术,如下图所示: LLM幻觉缓减技术值得注意的是: 检索增强生成&…...

C# 使用多线程,关闭窗体时,退出所有线程
this.Close(); 只是关闭当前窗口,若不是主窗体的话,是无法退出程序的,另外若有托管线程(非主线程),也无法干净地退出;Application.Exit(); 强制所有消息中止,退出所有的窗体&…...

数据结构实验6:图的应用
目录 一、实验目的 1. 邻接矩阵 2. 邻接矩阵表示图的结构定义 3. 图的初始化 4. 边的添加 5. 边的删除 6. Dijkstra算法 三、实验内容 实验内容 代码 截图 分析 一、实验目的 1.掌握图的邻接矩阵的存储定义; 2.掌握图的最短路径…...

Spring Boot整合JUnit
引言 测试是软件开发过程中不可或缺的一环,而JUnit作为Java生态中最流行的测试框架之一,与Spring Boot的整合为开发者提供了一套强大的测试工具。本文将讨论Spring Boot整合JUnit的技术细节、最佳实践以及测试驱动开发(TDD)的优雅…...

uniapp写小程序实现清除缓存(存储/获取/移除/清空)
在uni-app中,可以使用uni.setStorageSync和uni.getStorageSync来进行数据的存储和获取。而移除缓存数据可以使用uni.removeStorageSync,清空缓存数据可以使用uni.clearStorageSync。 以下是使用示例: 存储数据: uni.setStorage…...

js菜单隐藏显示
1、树状结构对应的表: 2、生成menulist的SQL语句 select {"id":"MenuID","parent":"ParentID","FirstLvMenu":"FirstLvMenu", "text":"MenuName","url":"MenuUrl",&quo…...

学习Spring的第五天(Bean的依赖注入)
Bean的依赖注入有两种方式: 一 . 常规Bean的依赖注入 很简单,不过多赘述了,注意ref: 是构造函数或set方法的参数,一般为对象, value: 是构造函数或set方法的参数,一般为值. 看下图 1.1 下面来演示一下集合数据类型的关于Bean的依赖注入 1.1.1这是List的注入(演示泛型为Strin…...

GAN在图像数据增强中的应用
在图像数据增强领域,生成对抗网络(GAN)的应用主要集中在通过生成新的图像数据来扩展现有数据集的规模和多样性。这种方法特别适用于训练数据有限的情况,可以通过增加数据的多样性来提高机器学习模型的性能和泛化能力。 以下是GAN在…...

Git推送本地文件到仓库
1. 在 Gitee 上创建一个新的仓库: 登录到 Gitee(https://gitee.com)账号。在 Gitee 主页上选择 "新建仓库" 或类似选项。输入仓库名称和描述,并选择其他相关选项(如公开/私有)。确认创建仓库 …...

Django笔记(一):环境部署
目录 Python虚拟环境 安装virtualenv 创建环境 激活环境 关闭: 安装Django VSCode配置 Python插件 Django插件 解释器选择 Django部署 创建项目 创建app 创建模板 编写视图 编写路由 启动服务器 访问 Python虚拟环境 安装virtualenv pip i…...