基于Spring自动注入快速实现策略模式+工厂模式优化过多的if..else
一、策略模式
1.1策略模式定义
在策略模式(Strategy Pattern)中一个类的行为或其算法可以在运行时更改。这种类型的设计模式属于行为型模式。
在策略模式定义了一系列算法或策略,并将每个算法封装在独立的类中,使得它们可以互相替换。通过使用策略模式,可以在运行时根据需要选择不同的算法,而不需要修改客户端代码。
在策略模式中,我们创建表示各种策略的对象和一个行为随着策略对象改变而改变的 context 对象。策略对象改变 context 对象的执行算法。
1.2 使用场景
1、如果在一个系统里面有许多类,它们之间的区别仅在于它们的行为,那么使用策略模式可以动态地让一个对象在许多行为中选择一种行为。
2、一个系统需要动态地在几种算法中选择一种。
3、如果一个对象有很多的行为,如果不用恰当的模式,这些行为就只好使用多重的条件选择语句来实现。
二、工厂模式
2.1工厂模式定义
工厂模式(Factory Pattern)是 Java 中最常用的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
工厂模式提供了一种将对象的实例化过程封装在工厂类中的方式。通过使用工厂模式,可以将对象的创建与使用代码分离,提供一种统一的接口来创建不同类型的对象。
在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。
2.2 使用场景
1、日志记录器:记录可能记录到本地硬盘、系统事件、远程服务器等,用户可以选择记录日志到什么地方。
2、数据库访问,当用户不知道最后系统采用哪一类数据库,以及数据库可能有变化时。
3、设计一个连接服务器的框架,需要三个协议,"POP3"、"IMAP"、"HTTP",可以把这三个作为产品类,共同实现一个接口。
注意事项:作为一种创建类模式,在任何需要生成复杂对象的地方,都可以使用工厂方法模式。有一点需要注意的地方就是复杂对象适合使用工厂模式,而简单对象,特别是只需要通过 new 就可以完成创建的对象,无需使用工厂模式。如果使用工厂模式,就需要引入一个工厂类,会增加系统的复杂度。
三、实现
需求为用户在美团上购买物品后的核销业务,根据一个周期内(周期为时间,如一个月,一周等)不同核销完成后的返回结果进行核销限制添加积分等,例如是否是本公司app下的会员,是否添加本公司企微,是否关注本公司公众号来进行对应操作,(后续可能会添加其他核销方式),所以该处可利用策略模式实现,而不是一长串的if..else进行功能的实现,违反了开闭原则(对扩展开放,对修改封闭),
3.1 使用if...else实现功能伪代码
过多嵌套,后续扩展功能不利于维护,实现简单
public class StrategyTest {@Resourceprivate TbUserDao tbUserDao;@Resourceprivate TbActivityDao tbActivityDao;@Resourceprivate MtCouponService mtCouponService;@Testpublic Object verify (String userId,String receipt_code) {Map<String,Object> result = new HashMap<>();//1. 获取用户信息TbUser tbUser = tbUserDao.selectUserByUserId(userId);if (null == tbUser) {return null;}// 根据receipt_code 和 skuId 查询出需要核销的类型// 2. 根据不同的核销次数进行核销限制String callBackMsg = mtCouponService.getCoinByReceiptCode(receipt_code, userId, "杭州");Map callBackMap = JSON.parseObject(callBackMsg, Map.class);if ("200".equals(callBackMap.get("code"))) {//2.1. 判断次数进行选择if(.....) {...}else if(....){...} else if(....){...} else if(....){...} }result.put("code", "505");result.put("msg", callBackMap.get("msg"));return result;}
}
3.2 使用策略模式+工厂模式
涉及到实际业务,服务实现直接简化为打印相应核销策略
抽象策略类接口
public interface VerificationService {String verificationName();void verify();
}
实现具体策略服务
- MtCouponService 核销服务
- UserService 用户服务
- LinkedWeChatService RPC判断用户状态服务
@Service
public class OfficialAccountsVerifyServiceImpl implements VerificationService{@Resourceprivate UserService userService;@Resourceprivate MtCouponService mtCouponService;@Overridepublic String verificationName() {return VerificationEnum.OfficialAccounts.getName();}@Overridepublic Object verify(String receipt_code, String userId,String cityName) {Map<String, Object> resultMap = new HashMap<>();TbUser tbUser = userService.selectUserByUserId(userId);if (null == tbUser) {return JSON.toJSONString(new CommonView("500", "用户不存在", null));}String response = HttpUtil.get("http://localhost:9088/wx/wxUserTags/isSubcribe?unionId=" + tbUser.getThirdUserId());Map result = JSON.toJavaObject(JSON.parseObject(response), Map.class);Object data = result.get("data");if ("true".equals(data.toString())) {return mtCouponService.getCoinByReceiptCode(receipt_code, userId ,cityName);}resultMap.put("code", "501");resultMap.put("msg", "请关注公众号后再进行核销!");return resultMap;}
}
该功能还在完善
@Service
public class VipVerifyServiceImpl implements VerificationService{@Resourceprivate MtCouponService mtCouponService;@Overridepublic String verificationName() {return VerificationEnum.Vip.getName();}@Overridepublic Object verify(String receipt_code, String userId,String cityName) {System.out.println("会员核销 = ");Map<String, Object> resultMap = new HashMap<>();resultMap.put("code", "501");resultMap.put("msg", "请开通Vip后再进行核销!");return resultMap;}
}
@Service
public class WeComGroupVerifyServiceImpl implements VerificationService{@Resourceprivate LinkedWeChatService linkedWeChatService;@Resourceprivate MtCouponService mtCouponService;@Overridepublic String verificationName() {return VerificationEnum.WeComGroup.getName();}@Overridepublic Object verify(String receipt_code, String userId,String cityName) {Map<String, Object> resultMap = new HashMap<>();boolean status = linkedWeChatService.existsCustomerInGroup(userId);if (status) { //入群return mtCouponService.getCoinByReceiptCode(receipt_code, userId ,cityName);}resultMap.put("code", "501");resultMap.put("msg", "请加群后再进行核销!");return resultMap;}
}
@Service
public class WeComVerifyServiceImpl implements VerificationService{@Resourceprivate LinkedWeChatService linkedWeChatService;@Resourceprivate MtCouponService mtCouponService;@Overridepublic String verificationName() {return VerificationEnum.WeCom.getName();}@Overridepublic Object verify(String receipt_code, String userId,String cityName) {Map<String, Object> resultMap = new HashMap<>();boolean status = linkedWeChatService.existsUnionId(userId);if (status) { //添加企微return mtCouponService.getCoinByReceiptCode(receipt_code, userId ,cityName);}resultMap.put("code", "501");resultMap.put("msg", "请添加企微后再进行核销!");return resultMap;}
}
环境类
/*** 核销枚举类*/
public enum VerificationEnum {/*** 默认*/defaultVerify( "defaultVerify"),/*** 企微*/WeCom( "WeCom"),/*** 入群*/WeComGroup("WeComGroup"),/*** 公众号*/OfficialAccounts( "OfficialAccounts"),/*** 会员*/Vip("Vip");private String name;VerificationEnum(String name) {this.name = name;}public String getName() {return name;}public void setName(String name) {this.name = name;}
}
/*** 初始化,并获取VerificationService所有服务bean*/
@Component
@Slf4j
public class StrategyHandler implements InitializingBean, ApplicationContextAware {/*** 存放策略的map,可以理解为策略的注册中心*/@Resourceprivate final Map<String, VerificationService> strategyServiceMap = new ConcurrentHashMap<>(16);/*** spring的上下文*/private ApplicationContext applicationContext;private static final Integer DAY_BETWEEN = -60;@Overridepublic void afterPropertiesSet() {//初始化把所有的策略bean放进ioc,使用的时候获取Map<String, VerificationService> matchBeans = applicationContext.getBeansOfType(VerificationService.class);//策略注入的bean做key,策略实现类做valuematchBeans.forEach((key, value) -> {strategyServiceMap.put(value.verificationName(), value);
// log.info("项目启动时初始化核销策略模式为: key={},value={}", key, value);});}/**** @param applicationContext spring的上下文* @throws BeansException 获取bean异常*/@Overridepublic void setApplicationContext(@NotNull(message = "bean不能为空") ApplicationContext applicationContext) throws BeansException {this.applicationContext = applicationContext;}/*** 通过key获取对应的策略实现** @param verifyNum 根据次数选择对应核销方式 (String类型或者整形都行,保持和策略接口一致就行)* @return VerificationService 具体的核销服务*/public Object getStrategy(int verifyNum, String receipt_code, String userId,String cityName) throws RuntimeException{switch (verifyNum) {case 0:case 1:log.info("使用默认核销");return strategyServiceMap.get(VerificationEnum.defaultVerify.getName()).verify(receipt_code,userId,cityName);case 2:log.info("当前核销次数为3次--->WeCom");return strategyServiceMap.get(VerificationEnum.WeCom.getName()).verify(receipt_code,userId,cityName);case 3:log.info("当前核销次数为4次--->WeComGroup");return strategyServiceMap.get(VerificationEnum.WeComGroup.getName()).verify(receipt_code,userId,cityName);case 4:log.info("当前核销次数为5次--->OfficialAccounts");return strategyServiceMap.get(VerificationEnum.OfficialAccounts.getName()).verify(receipt_code,userId,cityName);
// case 5:
// strategyServiceMap.get(VerificationEnum.Vip.getName()).verify(userId);
// break;default:log.info("当前核销次数为6次及以上--->禁止核销");break;}CouponResult result = new CouponResult();result.setCode("501");result.setMsg("当前验券次数超过6次,该周期禁止核销");result.setValue(0);return JSON.toJSONString(result);}
*初始化项目时直接注册服务,方便调用
调用方便,易扩展,但是实现过程较繁琐,
@PostMapping(value = "/verify")@SignCtrl@ResponseBody@ApiResponses(value = {@ApiResponse(code = 400, message = "验券未通过"),@ApiResponse(code = 500, message = "用户不存在"),@ApiResponse(code = 501, message = "禁止核验"),@ApiResponse(code = 501, message = "券码不存在"),@ApiResponse(code = 1003, message = "库存不足"),@ApiResponse(code = 1006, message = "券码输入错误"),@ApiResponse(code = 1008, message = "券码已使用过"),@ApiResponse(code = 1009, message = "券码已失效"),@ApiResponse(code = 1010, message = "退券中的消费券"),@ApiResponse(code = 1011, message = "已退券的消费券"),@ApiResponse(code = 1012, message = "此团购券不可在本店消费,请确认正确分店")})public Object verify(String receipt_code,String userId,String cityName) {Map mtCoupon = mongoTemplate.findOne(new Query(Criteria.where("receiptCode").is(receipt_code)),Map.class, "tbopen_shop_uuid");List<TbActivity> tbActivities;if (mtCoupon != null) {Calendar instance = Calendar.getInstance();instance.setTime(new Date());instance.add(Calendar.DATE, DAY_BETWEEN);tbActivities = activityDao.selectMTOrderByUserId(userId,DateUtils.getNowDate(instance.getTime()), DateUtils.formatDate(new Date()));int verify = 0;if (tbActivities != null) {Object[] receipt_codeArray = tbActivities.stream().map(TbActivity::getActivitySource).toArray();List<Map> verifyList = mongoTemplate.find(new Query(Criteria.where("receiptCode").in(receipt_codeArray).and("skuId").is(mtCoupon.get("skuId"))),Map.class, "tbopen_shop_uuid");verify = verifyList.size();}return strategyHandler.getStrategy(verify, receipt_code, userId, cityName);}HashMap<Object, Object> map = new HashMap<>();map.put("msg", "券码不存在");return JSON.toJSONString(new CommonView("502","成功", map));}
四 总结
4.1 优点
- 提高了代码的复用性和可维护性,将算法的定义与其具体实现进行解耦。
- 可以在运行时动态替换算法,提高了程序的灵活性。
- 符合开闭原则,新增算法无需修改现有代码。
4.2 缺点
- 客户端需要知道所有的策略类,并根据具体场景选择合适的策略,增加了客户端的复杂度。
- 如果策略类较多,会导致类的数量增多,增加系统的复杂度。
4.3 适用场景
- 当一个系统中存在多个类只有它们的行为或算法不同时。
- 当一个类定义了多种行为,而这些行为在这个类的操作中以多个条件语句的形式出现,可以将相关的条件分支移入它们各自的策略类中,以替换这些条件语句。
- 当系统需要动态地在几种算法中选择一种时,如根据不同的配置、用户选择或者环境条件等。
相关文章:

基于Spring自动注入快速实现策略模式+工厂模式优化过多的if..else
一、策略模式 1.1策略模式定义 在策略模式(Strategy Pattern)中一个类的行为或其算法可以在运行时更改。这种类型的设计模式属于行为型模式。 在策略模式定义了一系列算法或策略,并将每个算法封装在独立的类中,使得它们可以互相…...

安装vcpkg管理opencv的安装+MFC缺失的解决
第一步,出现#include没有办法找到opencv头文件的问题,无法解决 在VC的提示下,安装了vcpkg,然后用vcpkg命令来帮助安装opencv,过程十分顺利。 1. cmd 到命令行窗口; 2. 建立src文件夹,并进入…...

了解树和学习二叉树
1.树 1.1 概念 树是一种 非线性 的数据结构,它是由 n ( n>0 )个有限结点组成一个具有层次关系的集合。 把它叫做树是因为它看 起来像一棵倒挂的树,也就是说它是根朝上,而叶朝下的 。 注意:树形结构中…...

Spring Boot学习随笔- 拦截器实现和配置(HandlerInterceptor、addInterceptors)、jar包部署和war包部署
学习视频:【编程不良人】2021年SpringBoot最新最全教程 第十三章、拦截器 拦截器 :Interceptor 拦截 中断 类似于javaweb中的Filter,不过没有Filter那么强大 作用 Spring MVC的拦截器是一种用于在请求处理过程中进行预处理和后处理的机制。拦…...

Pipelined-ADC设计二——结构指标及非理想因素(Part2)
接上文,本章将两个比较重要的非理想因素,因此各项指标制定。后续会对常见的非理想因素给出常见的解决方法,以及设计所采用的方法。 2.2.7. 比较器失调 在流水线 ADC 中,比较器的主要误差来源就是比较器失调,称为失调误…...

Ubuntu 常用命令之 clear 命令用法介绍
📑Linux/Ubuntu 常用命令归类整理 clear命令在Ubuntu系统下用于清除终端屏幕的内容。这个命令没有任何参数,它的主要作用就是清理终端屏幕上的所有信息,使得屏幕看起来像是新打开的一样。 使用clear命令非常简单,只需要在终端中…...

【JAVA面试题】什么是对象锁?什么是类锁?
🍎 个人博客 :个 人 主 页 🏆个人专栏:多线程JAVA ⛳️ 功 不 唐 捐 ,玉 汝 于 成 目录 前言 回答 对象锁(Object Lock): 类锁(Class Lock)࿱…...

飞天使-k8s知识点5-kubernetes基础名词扫盲
文章目录 deploymentspodNodeserviceskubectl 实现应用伸缩kubectl 实现滚动更新kubernetes架构 deployments 中文文档 http://docs.kubernetes.org.cn/251.htmldeployment是用来创建和更新应用的,master 会负责将创建好的应用实例调度到集群中的各个节点 应用实例…...

【视觉实践】使用Mediapipe进行目标检测:杯子检测和椅子检测实践
目录 1 Mediapipe 2 Solutions 3 安装mediapipe 4 实践 1 Mediapipe Mediapipe是google的一个开源项目,可以提供开源的、跨平台的常用机器学习(machine learning,ML)方案。MediaPipe是一个用于构建机器学习管道</...

C++之深拷贝进阶
目录 拷贝构造函数的深拷贝进阶版本 赋值运算符重载的深拷贝进阶 总结 上期我们学习了C中深拷贝的传统版本,今天我们将学习更为高效的版本。 拷贝构造函数的深拷贝进阶版本 传统版本代码如下: string(string& s):_str(new char[strlen(s._str)…...
导行电磁波从纵向场分量求其他方向分量的矩阵表示
导行电磁波从纵向场分量求解其他方向分量的矩阵表示 导行电磁波传播的特点 电磁波在均匀、线性、各向同性的空间中沿着 z z z轴传播,可用分离变量法将时间轴、 z z z轴与 x , y x,y x,y轴分离,电磁波的形式可表示为: E ⃗ E ⃗ ( x , y )…...

融资项目——swagger2的注解
1. ApiModel与ApiModelProperty(在实体类中使用) 如上图,ApiModel加在实体类上方,用于整体描述实体类。ApiModelProperty(value"xxx",example"xxx")放于每个属性上方,用于对属性进行描述。swagger2网页上的效果如下图&am…...

【性能优化】MySql数据库查询优化方案
阅读本文你的收获 了解系统运行效率提升的整体解决思路和方向学会MySQl中进行数据库查询优化的步骤学会看慢查询、执行计划、进行性能分析、调优 一、问题:如果你的系统运行很慢,你有什么解决方案? 关于这个问题,我们通常首先…...

Chrome浏览器http自动跳https问题
现象: Chrome浏览器访问http页面时有时会自动跳转https,导致一些问题。比如: 开发阶段访问dev环境网址跳https,后端还是http,导致接口跨域。 复现: 先访问http网址,再改成https访问…...

【C++进阶02】多态
一、多态的概念及定义 1.1 多态的概念 多态简单来说就是多种形态 同一个行为,不同对象去完成时 会产生出不同的状态 多态分为静态多态和动态多态 静态多态指的是编译时 在程序编译期间确定了程序的行为 比如:函数重载 动态多态指的是运行时 在程序运行…...

PHP开发日志——循环和条件语句嵌套不同,效率不同(循环内加入条件语句,条件语句判断后加入循环,array_map函数中加入条件语句)
十多年前开发框架时,为了效率不断试过各种代码写法,今天又遇到了,想想php8时代会不会有所变化,结果其实也还是和当年一样,但当年没写博客,但现在可以把数据记录下来了。 PHP_loop_ireflies_dark_forest 项目…...

【Seata源码学习 】 扫描@GlobalTransaction注解 篇一
1. SeataAutoConfiguration 自动配置类的加载 基于SpringBoot的starter机制,在应用上下文启动时,会加载SeataAutoConfiguration自动配置类 # Auto Configure org.springframework.boot.autoconfigure.EnableAutoConfigurationio.seata.spring.boot.aut…...

DBA-MySql面试问题及答案-上
文章目录 1.什么是数据库?2.如何查看某个操作的语法?3.MySql的存储引擎有哪些?4.常用的2种存储引擎?6.可以针对表设置引擎吗?如何设置?6.选择合适的存储引擎?7.选择合适的数据类型8.char & varchar9.Mysql字符集10.如何选择…...

网络爬虫之Ajax动态数据采集
动态数据采集 规则 有时候我们在用 requests 抓取页面的时候,得到的结果可能和在浏览器中看到的不一样,在浏览器中可以看到正常显示的页面教据,但是使用 requests 得到的结果并没有,这是因为requests 获取的都是原始的 HTML 文档…...

c语言的初始学习(练习)
##初学c语言---MOOC浙江大学翁恺先生学习c语言 那么我们先看看这个题目吧,这是初始语法的应用。 记住,我们的程序是按步骤执行的,并不是在不同的两行同时进行。 程序设计:1.了解题目的需要,几个变量需要用到&#x…...

转转集团旗下首家二手多品类循环仓店“超级转转”开业
6月9日,国内领先的循环经济企业转转集团旗下首家二手多品类循环仓店“超级转转”正式开业。 转转集团创始人兼CEO黄炜、转转循环时尚发起人朱珠、转转集团COO兼红布林CEO胡伟琨、王府井集团副总裁祝捷等出席了开业剪彩仪式。 据「TMT星球」了解,“超级…...

高等数学(下)题型笔记(八)空间解析几何与向量代数
目录 0 前言 1 向量的点乘 1.1 基本公式 1.2 例题 2 向量的叉乘 2.1 基础知识 2.2 例题 3 空间平面方程 3.1 基础知识 3.2 例题 4 空间直线方程 4.1 基础知识 4.2 例题 5 旋转曲面及其方程 5.1 基础知识 5.2 例题 6 空间曲面的法线与切平面 6.1 基础知识 6.2…...

html-<abbr> 缩写或首字母缩略词
定义与作用 <abbr> 标签用于表示缩写或首字母缩略词,它可以帮助用户更好地理解缩写的含义,尤其是对于那些不熟悉该缩写的用户。 title 属性的内容提供了缩写的详细说明。当用户将鼠标悬停在缩写上时,会显示一个提示框。 示例&#x…...

Unity UGUI Button事件流程
场景结构 测试代码 public class TestBtn : MonoBehaviour {void Start(){var btn GetComponent<Button>();btn.onClick.AddListener(OnClick);}private void OnClick(){Debug.Log("666");}}当添加事件时 // 实例化一个ButtonClickedEvent的事件 [Formerl…...

Golang——7、包与接口详解
包与接口详解 1、Golang包详解1.1、Golang中包的定义和介绍1.2、Golang包管理工具go mod1.3、Golang中自定义包1.4、Golang中使用第三包1.5、init函数 2、接口详解2.1、接口的定义2.2、空接口2.3、类型断言2.4、结构体值接收者和指针接收者实现接口的区别2.5、一个结构体实现多…...

永磁同步电机无速度算法--基于卡尔曼滤波器的滑模观测器
一、原理介绍 传统滑模观测器采用如下结构: 传统SMO中LPF会带来相位延迟和幅值衰减,并且需要额外的相位补偿。 采用扩展卡尔曼滤波器代替常用低通滤波器(LPF),可以去除高次谐波,并且不用相位补偿就可以获得一个误差较小的转子位…...

Cilium动手实验室: 精通之旅---13.Cilium LoadBalancer IPAM and L2 Service Announcement
Cilium动手实验室: 精通之旅---13.Cilium LoadBalancer IPAM and L2 Service Announcement 1. LAB环境2. L2公告策略2.1 部署Death Star2.2 访问服务2.3 部署L2公告策略2.4 服务宣告 3. 可视化 ARP 流量3.1 部署新服务3.2 准备可视化3.3 再次请求 4. 自动IPAM4.1 IPAM Pool4.2 …...

数据分析六部曲?
引言 上一章我们说到了数据分析六部曲,何谓六部曲呢? 其实啊,数据分析没那么难,只要掌握了下面这六个步骤,也就是数据分析六部曲,就算你是个啥都不懂的小白,也能慢慢上手做数据分析啦。 第一…...
起重机起升机构的安全装置有哪些?
起重机起升机构的安全装置是保障吊装作业安全的关键部件,主要用于防止超载、失控、断绳等危险情况。以下是常见的安全装置及其功能和原理: 一、超载保护装置(核心安全装置) 1. 起重量限制器 功能:实时监测起升载荷&a…...
C++ 类基础:封装、继承、多态与多线程模板实现
前言 C 是一门强大的面向对象编程语言,而类(Class)作为其核心特性之一,是理解和使用 C 的关键。本文将深入探讨 C 类的基本特性,包括封装、继承和多态,同时讨论类中的权限控制,并展示如何使用类…...