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

基于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策略模式定义 在策略模式&#xff08;Strategy Pattern&#xff09;中一个类的行为或其算法可以在运行时更改。这种类型的设计模式属于行为型模式。 在策略模式定义了一系列算法或策略&#xff0c;并将每个算法封装在独立的类中&#xff0c;使得它们可以互相…...

安装vcpkg管理opencv的安装+MFC缺失的解决

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

了解树和学习二叉树

1.树 1.1 概念 树是一种 非线性 的数据结构&#xff0c;它是由 n &#xff08; n>0 &#xff09;个有限结点组成一个具有层次关系的集合。 把它叫做树是因为它看 起来像一棵倒挂的树&#xff0c;也就是说它是根朝上&#xff0c;而叶朝下的 。 注意&#xff1a;树形结构中…...

Spring Boot学习随笔- 拦截器实现和配置(HandlerInterceptor、addInterceptors)、jar包部署和war包部署

学习视频&#xff1a;【编程不良人】2021年SpringBoot最新最全教程 第十三章、拦截器 拦截器 &#xff1a;Interceptor 拦截 中断 类似于javaweb中的Filter&#xff0c;不过没有Filter那么强大 作用 Spring MVC的拦截器是一种用于在请求处理过程中进行预处理和后处理的机制。拦…...

Pipelined-ADC设计二——结构指标及非理想因素(Part2)

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

Ubuntu 常用命令之 clear 命令用法介绍

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

【JAVA面试题】什么是对象锁?什么是类锁?

&#x1f34e; 个人博客 &#xff1a;个 人 主 页 &#x1f3c6;个人专栏&#xff1a;多线程JAVA ⛳️ 功 不 唐 捐 &#xff0c;玉 汝 于 成 目录 前言 回答 对象锁&#xff08;Object Lock&#xff09;&#xff1a; 类锁&#xff08;Class Lock&#xff09;&#xff1…...

飞天使-k8s知识点5-kubernetes基础名词扫盲

文章目录 deploymentspodNodeserviceskubectl 实现应用伸缩kubectl 实现滚动更新kubernetes架构 deployments 中文文档 http://docs.kubernetes.org.cn/251.htmldeployment是用来创建和更新应用的&#xff0c;master 会负责将创建好的应用实例调度到集群中的各个节点 应用实例…...

【视觉实践】使用Mediapipe进行目标检测:杯子检测和椅子检测实践

目录 1 Mediapipe 2 Solutions 3 安装mediapipe 4 实践 1 Mediapipe Mediapipe是google的一个开源项目,可以提供开源的、跨平台的常用机器学习(machine learning,ML)方案。MediaPipe是一个用于构建机器学习管道</...

C++之深拷贝进阶

目录 拷贝构造函数的深拷贝进阶版本 赋值运算符重载的深拷贝进阶 总结 上期我们学习了C中深拷贝的传统版本&#xff0c;今天我们将学习更为高效的版本。 拷贝构造函数的深拷贝进阶版本 传统版本代码如下&#xff1a; string(string& s):_str(new char[strlen(s._str)…...

导行电磁波从纵向场分量求其他方向分量的矩阵表示

导行电磁波从纵向场分量求解其他方向分量的矩阵表示 导行电磁波传播的特点 电磁波在均匀、线性、各向同性的空间中沿着 z z z轴传播&#xff0c;可用分离变量法将时间轴、 z z z轴与 x , y x,y x,y轴分离&#xff0c;电磁波的形式可表示为&#xff1a; E ⃗ E ⃗ ( x , y )…...

融资项目——swagger2的注解

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

【性能优化】MySql数据库查询优化方案

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

Chrome浏览器http自动跳https问题

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

【C++进阶02】多态

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

PHP开发日志——循环和条件语句嵌套不同,效率不同(循环内加入条件语句,条件语句判断后加入循环,array_map函数中加入条件语句)

十多年前开发框架时&#xff0c;为了效率不断试过各种代码写法&#xff0c;今天又遇到了&#xff0c;想想php8时代会不会有所变化&#xff0c;结果其实也还是和当年一样&#xff0c;但当年没写博客&#xff0c;但现在可以把数据记录下来了。 PHP_loop_ireflies_dark_forest 项目…...

【Seata源码学习 】 扫描@GlobalTransaction注解 篇一

1. SeataAutoConfiguration 自动配置类的加载 基于SpringBoot的starter机制&#xff0c;在应用上下文启动时&#xff0c;会加载SeataAutoConfiguration自动配置类 # Auto Configure org.springframework.boot.autoconfigure.EnableAutoConfigurationio.seata.spring.boot.aut…...

DBA-MySql面试问题及答案-上

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

网络爬虫之Ajax动态数据采集

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

c语言的初始学习(练习)

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

stm32G473的flash模式是单bank还是双bank?

今天突然有人stm32G473的flash模式是单bank还是双bank&#xff1f;由于时间太久&#xff0c;我真忘记了。搜搜发现&#xff0c;还真有人和我一样。见下面的链接&#xff1a;https://shequ.stmicroelectronics.cn/forum.php?modviewthread&tid644563 根据STM32G4系列参考手…...

在鸿蒙HarmonyOS 5中实现抖音风格的点赞功能

下面我将详细介绍如何使用HarmonyOS SDK在HarmonyOS 5中实现类似抖音的点赞功能&#xff0c;包括动画效果、数据同步和交互优化。 1. 基础点赞功能实现 1.1 创建数据模型 // VideoModel.ets export class VideoModel {id: string "";title: string ""…...

华为OD机试-食堂供餐-二分法

import java.util.Arrays; import java.util.Scanner;public class DemoTest3 {public static void main(String[] args) {Scanner in new Scanner(System.in);// 注意 hasNext 和 hasNextLine 的区别while (in.hasNextLine()) { // 注意 while 处理多个 caseint a in.nextIn…...

QT: `long long` 类型转换为 `QString` 2025.6.5

在 Qt 中&#xff0c;将 long long 类型转换为 QString 可以通过以下两种常用方法实现&#xff1a; 方法 1&#xff1a;使用 QString::number() 直接调用 QString 的静态方法 number()&#xff0c;将数值转换为字符串&#xff1a; long long value 1234567890123456789LL; …...

【JavaWeb】Docker项目部署

引言 之前学习了Linux操作系统的常见命令&#xff0c;在Linux上安装软件&#xff0c;以及如何在Linux上部署一个单体项目&#xff0c;大多数同学都会有相同的感受&#xff0c;那就是麻烦。 核心体现在三点&#xff1a; 命令太多了&#xff0c;记不住 软件安装包名字复杂&…...

Spring AI与Spring Modulith核心技术解析

Spring AI核心架构解析 Spring AI&#xff08;https://spring.io/projects/spring-ai&#xff09;作为Spring生态中的AI集成框架&#xff0c;其核心设计理念是通过模块化架构降低AI应用的开发复杂度。与Python生态中的LangChain/LlamaIndex等工具类似&#xff0c;但特别为多语…...

安全突围:重塑内生安全体系:齐向东在2025年BCS大会的演讲

文章目录 前言第一部分&#xff1a;体系力量是突围之钥第一重困境是体系思想落地不畅。第二重困境是大小体系融合瓶颈。第三重困境是“小体系”运营梗阻。 第二部分&#xff1a;体系矛盾是突围之障一是数据孤岛的障碍。二是投入不足的障碍。三是新旧兼容难的障碍。 第三部分&am…...

20个超级好用的 CSS 动画库

分享 20 个最佳 CSS 动画库。 它们中的大多数将生成纯 CSS 代码&#xff0c;而不需要任何外部库。 1.Animate.css 一个开箱即用型的跨浏览器动画库&#xff0c;可供你在项目中使用。 2.Magic Animations CSS3 一组简单的动画&#xff0c;可以包含在你的网页或应用项目中。 3.An…...

如何更改默认 Crontab 编辑器 ?

在 Linux 领域中&#xff0c;crontab 是您可能经常遇到的一个术语。这个实用程序在类 unix 操作系统上可用&#xff0c;用于调度在预定义时间和间隔自动执行的任务。这对管理员和高级用户非常有益&#xff0c;允许他们自动执行各种系统任务。 编辑 Crontab 文件通常使用文本编…...

【Android】Android 开发 ADB 常用指令

查看当前连接的设备 adb devices 连接设备 adb connect 设备IP 断开已连接的设备 adb disconnect 设备IP 安装应用 adb install 安装包的路径 卸载应用 adb uninstall 应用包名 查看已安装的应用包名 adb shell pm list packages 查看已安装的第三方应用包名 adb shell pm list…...