[设计模式]springboot优雅实现策略器模式(加入注册器实现)

优雅实现策略器模式

  • 场景
  • 废话少说,源码地址。
  • 讲解
    • 关于本文
    • 创建登陆策略
    • 创建环境(注册器)
      • 简化代码 创建父类对象,也方便后期扩展
    • 具体策略实现类
      • 用户名密码登陆
      • 手机号登陆
    • 触发点
    • 调试
    • 简言

场景

登陆场景使用(登陆之后返回用户信息和token所做操作基本一致,杜绝多个判断)

  1. 用户名密码登陆,
  2. 指纹登录
  3. 手机号登陆
    楼主之前写过一次通过注解实现的,那个看起来没有注册器实现优雅。
    策略器可以使用的场景很多,主要为了杜绝多if判断,把if判断需实现的信息放在自己的实现类中,提升代码可阅读性,提高执行效率,降低出错概率。
    八股文:可扩展性高,灵活性高,代码复用性高,单一职责原则,可替换性高。

废话少说,源码地址。

代码地址 
git clone https://github.com/gwy572294624/strategy-demo-new

讲解

chatGpt关于策略模式的八股文
策略模式(Strategy Pattern)是一种行为型设计模式,用于将不同的算法封装成独立的策略类,并使这些策略类可以相互替换,以实现在运行时动态地选择不同的算法。

在策略模式中,有三个主要的角色:

  1. 环境(Context):环境类是策略模式的核心,其内部持有一个策略对象的引用。环境类将具体的任务委派给策略对象进行处理,而不直接实现具体的算法逻辑。

  2. 策略(Strategy):策略类是一个接口或抽象类,它定义了具体算法的方法。不同的策略类实现了不同的算法逻辑。策略类之间可以相互替换,提供了算法的灵活配置。

  3. 具体策略(Concrete Strategies):具体策略类是策略模式的实现类,实现了策略接口或抽象类中定义的具体算法逻辑。

策略模式的工作流程如下:

  1. 客户端创建一个环境对象,并通过构造函数或设置方法将具体的策略对象传递给环境对象。

  2. 客户端根据需求选择合适的策略对象。

  3. 环境对象在执行任务时,会将具体的算法委派给当前持有的策略对象进行处理。

  4. 当需要切换算法时,客户端可以动态地替换环境对象的策略对象,实现不同的算法逻辑。

策略模式的优点包括灵活性、可替换性、可扩展性和代码复用性。它可以避免使用大量的条件语句或开关语句,将不同的算法逻辑封装在独立的策略类中,使系统更加灵活、易于扩展和维护。

关于本文

策略模式组成是通过环境,策略,具体实现三部分组成,本文将环境(context)通过注册器实现,实现自动化注入容器中,维护起来更方便。主要用于优化系统中多if判断场景,大一点的系统应该都能碰到,所以希望可以帮助到大家。

创建登陆策略


public interface LoginStrategy {/*** 检查参数* @return*/default Result checkParam(LoginPatamDTO loginPatamDTO){return Result.err();}/*** 登陆* @param loginPatamDTO* @return*/default Result<LoginUserInfoVO> login(LoginPatamDTO loginPatamDTO){return Result.err();}
}

创建环境(注册器)

将bean对象放在指定的map容器中。


@Component
public class LoginCommonStrategyRegistry {@Autowiredprivate BeanFactory beanFactory;private Map<LoginTypeEnum, LoginStrategy> loginrCommonStrategyMap = new ConcurrentHashMap<>();public Map<LoginTypeEnum, LoginStrategy> mapGet() {return this.loginrCommonStrategyMap;}/*** 初始化策略*/public void registry(LoginTypeEnum loginTypeEnum, Class glass) {LoginStrategy funderCommonStrategy = (LoginStrategy)beanFactory.getBean(glass);this.loginrCommonStrategyMap.put(loginTypeEnum, funderCommonStrategy);}
}

简化代码 创建父类对象,也方便后期扩展

@PostConstruct 是 Java 中的一个注解,用于标记一个方法,在对象创建之后(通过构造函数创建并注入依赖后),在依赖注入完成之后立即执行。它的作用是在对象初始化阶段执行一些必要的操作,例如初始化资源、建立连接、加载数据等。


@Service
public abstract class AbstractLoginService implements LoginStrategy {private final LoginCommonStrategyRegistry loginCommonStrategyRegistry;public AbstractLoginService(LoginCommonStrategyRegistry loginCommonStrategyRegistry){this.loginCommonStrategyRegistry = loginCommonStrategyRegistry;}/*** 注册当前类到策略map中*/@PostConstructprotected abstract void initRegistry();
}

具体策略实现类

用户名密码登陆


@Service
@Slf4j
public class UserNameLoginService extends AbstractLoginService {public UserNameLoginService(LoginCommonStrategyRegistry loginCommonStrategyRegistry) {super(loginCommonStrategyRegistry);}@Overrideprotected void initRegistry() {super.loginCommonStrategyRegistry.registry(LoginTypeEnum.USERNAME_TYPE, this.getClass());}@Overridepublic Result checkParam(LoginPatamDTO loginPatamDTO) {if (ObjectUtils.isEmpty(loginPatamDTO.getUserName())) {return Result.err("用户名不能为空");}if (ObjectUtils.isEmpty(loginPatamDTO.getPassword())) {return Result.err("密码不能为空");}return Result.suc();}@Overridepublic Result<LoginUserInfoVO> login(LoginPatamDTO loginPatamDTO) {log.info("这里是用户名和密码登陆");if (loginPatamDTO.getUserName().equals("admin") && loginPatamDTO.getPassword().equals("admin")) {// 创建默认对象 模拟登陆返回LoginUserInfoVO loginUserInfoVO = LoginUserInfoVO.loginUserInfoVOCreate(loginPatamDTO.getLoginTypeEnum());return Result.suc(loginUserInfoVO);} else {return Result.err("用户名或者密码不正确");}}
}

手机号登陆


@Service
@Slf4j
public class PhoneLoginService extends AbstractLoginService {public PhoneLoginService(LoginCommonStrategyRegistry loginCommonStrategyRegistry) {super(loginCommonStrategyRegistry);}@Overrideprotected void initRegistry() {super.loginCommonStrategyRegistry.registry(LoginTypeEnum.PHONE_TYPE, this.getClass());}@Overridepublic Result checkParam(LoginPatamDTO loginPatamDTO) {if (ObjectUtils.isEmpty(loginPatamDTO.getPhone())) {return Result.err("手机号不能为空");}if (ObjectUtils.isEmpty(loginPatamDTO.getSmsCode())) {return Result.err("验证码不能为空");}return Result.suc();}@Overridepublic Result<LoginUserInfoVO> login(LoginPatamDTO loginPatamDTO) {log.info("这里是手机号登陆");if (loginPatamDTO.getPhone().equals("17777777777") && loginPatamDTO.getSmsCode().equals("7777")) {// 创建默认对象 模拟登陆返回LoginUserInfoVO loginUserInfoVO = LoginUserInfoVO.loginUserInfoVOCreate(loginPatamDTO.getLoginTypeEnum());return Result.suc(loginUserInfoVO);} else {return Result.err("验证码不正确");}}
}

触发点

如果没有写策略模式 那代码应该是这样

if(type.equals("用户名登陆")){// 用户名密码登陆验证
}else if(type.equals("手机号登陆")){// 手机号登陆
}else if(.......){....
}

使用策略模式触发点确实简化了不少代码,让代码优雅起来了。后续扩展啥的都很方便。

@RestController
public class LoginController {/*** 模拟redis*/public static Map<String, String> userInforedis = new ConcurrentHashMap<>();private final LoginCommonStrategyRegistry loginCommonStrategyRegistry;public LoginController(LoginCommonStrategyRegistry loginCommonStrategyRegistry) {this.loginCommonStrategyRegistry = loginCommonStrategyRegistry;}@RequestMapping(value = "/a/login")public Result<LoginUserInfoVO> login(LoginPatamDTO loginPatamDTO) {if (ObjectUtils.isEmpty(loginPatamDTO.getLoginTypeEnum())) {return Result.err("无效的登陆方式");}LoginStrategy loginStrategy = loginCommonStrategyRegistry.mapGet().get(loginPatamDTO.getLoginTypeEnum());if (loginStrategy == null) {return Result.err("无效的登陆方式");}Result result = loginStrategy.checkParam(loginPatamDTO);if (result.getCode() == 0) {return result;}Result<LoginUserInfoVO> login = loginStrategy.login(loginPatamDTO);if (login.getCode() == 1) {userInforedis.put(login.getDate().getToken(), JSON.toJSONString(login.getDate()));}return login;}}

调试

http://127.0.0.1:8080/a/login?loginTypeEnum=USERNAME_TYPE&userName=admin&password=admin{"code":1,"msg":"成功","date":{"token":"57a44abe-e731-40f1-a9f6-1069710ac6fa","userInfoBO":{"userId":"这个是id","userRealName":"这个是名字","userSex":"这个是性别","userDeptIds":"这个是部门","loginTypeEnum":"USERNAME_TYPE"}}}

简言

在接入策略模式前一定要想好调用交互逻辑,如果系统有现成的最好使用现成的。当然也要避免循环依赖注入。

希望本文可以帮到大家。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/80805.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

AIGC(生成式AI)试用 5 -- 从模糊到精确,再一步

参考 AIGC&#xff08;生成式AI&#xff09;试用 4 -- 从模糊到精确_Rolei_zl的博客-CSDN博客 提问信息不足时&#xff0c;生成式AI有的会引导提问者给出更多信息&#xff0c;有的会按自己的理解给出一个在某些方面正确的答案 随着提供的信息越来越多&#xff0c;生成式AI给…

嵌入式Linux基础学习笔记目录

1. 嵌入式Linux应用开发基础知识 1.1 交叉编译 1.2 GCC编译器 1.3 makefire 1.4 文件I/O 1.5 Framebuffer应用编程 1.6 文字显示及图象显示 1.7 输入系统应用编程 1.8 网络编程 1.9 多线程编程 1.10 串口编程 1.11 I2C应用编程 2. 源码分析 2.1 MQTT源码 2.2 蓝牙源码 2.3 MJP…

算法通过村第七关-树(递归/二叉树遍历)黄金笔记|迭代遍历

文章目录 前言1. 迭代法实现前序遍历2. 迭代法实现中序遍历3. 迭代法实现后序遍历总结 前言 提示&#xff1a;在一个信息爆炸却多半无用的世界&#xff0c;清晰的见解就成了一种力量。 --尤瓦尔赫拉利《今日简史》 你是不是觉得上一关特别简单&#xff0c;代码少&#xff0c;背…

机器学习第六课--朴素贝叶斯

朴素贝叶斯广泛地应用在文本分类任务中&#xff0c;其中最为经典的场景为垃圾文本分类(如垃圾邮件分类:给定一个邮件&#xff0c;把它自动分类为垃圾或者正常邮件)。这个任务本身是属于文本分析任务&#xff0c;因为对应的数据均为文本类型&#xff0c;所以对于此类任务我们首先…

【Git】Git 变基(rebase)以及rebase和merge之间的区别

Git 变基 1.变基 — rebase 在 Git 中整合来自不同分支的修改主要有两种方法&#xff1a;merge 以及 rebase。 在前面的文章中已经介绍了merge&#xff0c;这里我们来学习另一个指令rebase。 变基的基本操作 回顾之前在 分支的合并 中的一个例子&#xff0c;在该例子中&am…

【面试刷题】——C++四种类型转化

C支持多种类型转换操作&#xff0c;其中包括四种主要类型转换方式&#xff1a; 隐式类型转换&#xff08;Implicit Conversion&#xff09;&#xff1a; 隐式类型转换是自动发生的类型转换&#xff0c;由编译器自动完成。 它用于处理不同数据类型之间的运算&#xff0c;例如将…

OpenCascade VTK STEP/IGES文件读取显示

OpenCascade & VTK STEP/IGES文件读取显示&#xff0c;OpenCascade读取转成STL&#xff0c;VTK显示STL。 Download - Open CASCADE Technology https://dev.opencascade.org/release 下载exe并安装&#xff0c;可以把source下载下来学习&#xff0c;官网速度慢&#xff0…

电商API的应用价值:淘宝1688京东API接口系列

API接口是一种软件应用程序&#xff0c;它充当两个不同软件应用程序之间的中介。它帮助不同的应用程序相互通信&#xff0c;共享数据&#xff0c;从而使用户能够完成不同的任务。API接口的用途非常广泛&#xff0c;下面是一些常见的用途&#xff1a; 数据共享&#xff1a;API接…

logstash通过kafka通道采集日志信息

1.修改文件/opt/app/elk/logstash-7.5.1/config.d/config1.conf&#xff0c;在input下添加kafka采集配置 #192.168.128.130:9103:kafka地址 #topics:主题 kafka {bootstrap_servers > ["192.168.128.130:9103"]group_id > "logstash"topics > [&…

python3对文件编码的转换处理

前言&#xff1a; 嗨喽~大家好呀&#xff0c;这里是魔王呐 ❤ ~! python更多源码/资料/解答/教程等 点击此处跳转文末名片免费获取 公司同事邀我一起给SQLSERVER 2008导数&#xff0c; 数据来源有高斯和ORACLE, 数据文件保存格式有UTF-8和GBK。 当我在做测试导入的时候发现S…

机器故障预测:未来24小时的决胜时刻!!!

一、背景介绍 这个竞赛的焦点是预测机器是否会在未来24小时内故障。数据包括与机器性能相关的各种特征&#xff0c;例如温度、振动、功耗和传感器读数。目标变量是二进制的&#xff0c;表示机器是否在未来24小时内故障&#xff08;1&#xff09;或未故障&#xff08;0&#xf…

2023年最新水果编曲软FL Studio Producer Edition 21.1.1.3750中文版功能介绍下载图文安装教程

各位&#xff0c;大家好&#xff0c;今天兔八哥给大家带来最新最新2023水果编曲软件FL Studio Producer Edition 21.1.1. Build 3750中文版中文版下载安装激活图文教程。我们一起先了解一些FL Studio 21 。FL Studio21是目前流行广泛使用人数最多音乐编曲宿主制作DAW软件&#…

【计算机视觉】Image Data Augmentation算法介绍合集(二)

文章目录 一、Random Grayscale二、GridMask三、Adversarial Color Enhancement四、Population Based Augmentation五、MaxUp六、SuperpixelGridCut, SuperpixelGridMean, SuperpixelGridMix七、InstaBoost八、Random Mix-up九、Sample Redistribution十、Batchboost十一、CutB…

SpringMVC之JSON返回及异常处理

目录 JSON处理 导入依赖 配置Spring-mvc.xml ResponseBody注解使用 测试 目录 JSON处理 导入依赖 配置Spring-mvc.xml ResponseBody注解使用 测试 Jackson 定义 用法 常用注解 统一异常处理 为什么要全局异常处理&#xff1f; 异常处理思路 SpringMVC异常分类 综…

【C++】类和对象核心总结

类和对象目录&#xff1a; 一、面向过程和面向对象初步认识 二、类的引入定义&#xff08;struct > class&#xff09; 2.1自定义类型 struct 和 class 的区别 2.2类放在内存中的什么存储区&#xff1f; 2.3类中函数定义的方式 2.3.1声明和定义分离&#xff08;增强代…

webpack:系统的了解webpack一些核心概念

文章目录 webpack 如何处理应用程序&#xff1f;何为webpack模块chunk&#xff1f;入口(entry)输出(output)loader开发loader 插件(plugin)简介流程插件开发&#xff1a;Tapable类监听(watching)compiler 钩子compilation 钩子compiler和compilation创建自定义 插件 loader和pl…

【Stable Diffusion】安装 Comfyui 之 window版

序言 由于stable diffusion web ui无法做到对流程进行控制&#xff0c;只是点击个生成按钮后&#xff0c;一切都交给AI来处理。但是用于生产生活是需要精细化对各个流程都要进行控制的。 故也就有个今天的猪脚&#xff1a;Comfyui 步骤 下载comfyui项目配置大模型和vae下载…

每日一题 77组合(剪枝)

题目 77 给定两个整数 n 和 k&#xff0c;返回范围 [1, n] 中所有可能的 k 个数的组合。 你可以按 任何顺序 返回答案。 示例 1&#xff1a; 输入&#xff1a;n 4, k 2 输出&#xff1a; [ [2,4], [3,4], [2,3], [1,2], [1,3], [1,4], ] 示例 2&#xff1a; 输入&#x…

图像数据通道格式:NCHW和NHWC的区别

在深度学习中&#xff0c;图像数据通道格式有两种&#xff1a; NCHW&#xff0c;又称&#xff1a;“channels_first”&#xff0c;是nvidia cudnn库原生支持的数据模式&#xff1b;在GPU中&#xff0c;使用NCHW格式计算卷积&#xff0c;比NHWC要快2.5倍左右&#xff08;0:54 v…

leetcode:69. x 的平方根

一、题目 函数原型&#xff1a;int mySqrt(int x) 二、思路 利用二分查找思想&#xff0c;在0与x区间进行查找。 设置左边界 left &#xff08;初始值为0&#xff09;&#xff0c;右边界 right&#xff08;初始值为x&#xff09;和中值 mid &#xff08;值为区间的中间值&#…