原来使用 Spring 实现策略模式可以这么简单!
策略模式作为一种软件设计模式,指对象有某个行为,但是在不同的场景中,该行为有不同的实现算法,可以替代代码中大量的 if-else。
比如我们生活中的场景:买东西结账可以使用微信支付、支付宝支付或者银行卡支付,这些交易方式就是不同的策略。
那么在什么时候使用策略模式呢?
在《阿里巴巴Java开发手册》中有提到当超过 3 层的 if-else 的逻辑判断代码可以使用策略模式来实现。
在 Spring 中实现策略模式的方式有很多种,下面通过一个案例来演示下,比如有个需求需要实现支持第三方登录,目前需要支持以下三种登录方式:
微信登录
QQ 登录
微博登录
下面将通过策略模式来实现这个需求,其中策略模式结构如下图所示:
策略模式结构如下图所示:
主要包括一个登录接口类和几种登录方式的实现方式,并利用简单工厂来获取对应的处理器。
定义策略接口
首先定义一个登录的策略接口 LoginHandler
,其中包括两个方法:
获取策略类型的方法
处理策略逻辑的方法
public interface LoginHandler<T extends Serializable> {/*** 获取登录类型** @return*/LoginType getLoginType();/*** 登录** @param request* @return*/LoginResponse<String, T> handleLogin(LoginRequest request);
}
其中,LoginHandler
的 getLoginType
方法用来获取登录的类型(即策略类型),用于根据客户端传递的参数直接获取到对应的策略实现。
客户端传递的相关参数都被封装为 LoginRequest,传递给 handleLogin 进行处理。
@Data
public class LoginRequest {private LoginType loginType;private Long userId;
}
其中,根据需求定义登录类型枚举如下:
public enum LoginType {QQ,WE_CHAT,WEI_BO;
}
实现策略接口
在定义好策略接口后,我们就需要根据各种第三方登录来实现对应的处理逻辑就可以了。
微信登录
@Component
public class WeChatLoginHandler implements LoginHandler<String> {private final Logger logger = LoggerFactory.getLogger(this.getClass());/*** 获取登录类型** @return*/@Overridepublic LoginType getLoginType() {return LoginType.WE_CHAT;}/*** 登录** @param request* @return*/@Overridepublic LoginResponse<String, String> handleLogin(LoginRequest request) {logger.info("微信登录:userId:{}", request.getUserId());String weChatName = getWeChatName(request);return LoginResponse.success("微信登录成功", weChatName);}private String getWeChatName(LoginRequest request) {return "wupx";}
}
QQ 登录
@Component
public class QQLoginHandler implements LoginHandler<Serializable> {private final Logger logger = LoggerFactory.getLogger(this.getClass());/*** 获取登录类型** @return*/@Overridepublic LoginType getLoginType() {return LoginType.QQ;}/*** 登录** @param request* @return*/@Overridepublic LoginResponse<String, Serializable> handleLogin(LoginRequest request) {logger.info("QQ登录:userId:{}", request.getUserId());return LoginResponse.success("QQ登录成功", null);}
}
微博登录
@Component
public class WeiBoLoginHandler implements LoginHandler<Serializable> {private final Logger logger = LoggerFactory.getLogger(this.getClass());/*** 获取登录类型** @return*/@Overridepublic LoginType getLoginType() {return LoginType.WEI_BO;}/*** 登录** @param request* @return*/@Overridepublic LoginResponse<String, Serializable> handleLogin(LoginRequest request) {logger.info("微博登录:userId:{}", request.getUserId());return LoginResponse.success("微博登录成功", null);}
}
创建策略的简单工厂
@Component
public class LoginHandlerFactory implements InitializingBean, ApplicationContextAware {private static final Map<LoginType, LoginHandler<Serializable>> LOGIN_HANDLER_MAP = new EnumMap<>(LoginType.class);private ApplicationContext appContext;/*** 根据登录类型获取对应的处理器** @param loginType 登录类型* @return 登录类型对应的处理器*/public LoginHandler<Serializable> getHandler(LoginType loginType) {return LOGIN_HANDLER_MAP.get(loginType);}@Overridepublic void afterPropertiesSet() throws Exception {// 将 Spring 容器中所有的 LoginHandler 注册到 LOGIN_HANDLER_MAPappContext.getBeansOfType(LoginHandler.class).values().forEach(handler -> LOGIN_HANDLER_MAP.put(handler.getLoginType(), handler));}@Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {appContext = applicationContext;}
}
我们让 LoginHandlerFactory
实现 InitializingBean
接口,在 afterPropertiesSet
方法中,基于 Spring 容器将所有 LoginHandler
自动注册到 LOGIN_HANDLER_MAP
,从而 Spring 容器启动完成后, getHandler 方法可以直接通过 loginType
来获取对应的登录处理器。
创建登录服务
在登录服务中,我们通过 LoginHandlerFactory
来获取对应的登录处理器,从而处理不同类型的第三方登录:
@Service
public class LoginServiceImpl implements LoginService {@Autowiredprivate LoginHandlerFactory loginHandlerFactory;@Overridepublic LoginResponse<String, Serializable> login(LoginRequest request) {LoginType loginType = request.getLoginType();// 根据 loginType 找到对应的登录处理器LoginHandler<Serializable> loginHandler =loginHandlerFactory.getHandler(loginType);// 处理登录return loginHandler.handleLogin(request);}
}
Factory 只负责获取 Handler,Handler 只负责处理具体的登录,Service 只负责逻辑编排,从而达到功能上的低耦合高内聚。
测试
写一个 Controller:
@RestController
public class LoginController {@Autowiredprivate LoginService loginService;/*** 登录*/@PostMapping("/login")public LoginResponse<String, Serializable> login(@RequestParam LoginType loginType, @RequestParam Long userId) {LoginRequest loginRequest = new LoginRequest();loginRequest.setLoginType(loginType);loginRequest.setUserId(userId);return loginService.login(loginRequest);}
}
然后用 Postman 测下下:
是不是很简单呢?如果需求又要加需求,需要支持 GitHub 第三方登录。
此时我们只需要添加一个新的策略实现,然后在登录枚举中加入对应的类型即可:
@Component
public class GitHubLoginHandler implements LoginHandler<Serializable> {private final Logger logger = LoggerFactory.getLogger(this.getClass());/*** 获取登录类型** @return*/@Overridepublic LoginType getLoginType() {return LoginType.GIT_HUB;}/*** 登录** @param request* @return*/@Overridepublic LoginResponse<String, Serializable> handleLogin(LoginRequest request) {logger.info("GitHub登录:userId:{}", request.getUserId());return LoginResponse.success("GitHub登录成功", null);}
}
此时不需要修改任何代码 ,因为 Spring 容器重启时会自动将 GitHubLoginHandler
注册到 LoginHandlerFactory
中,使用 Spring 实现策略模式就是这么简单,还不快学起来!
有道无术,术可成;有术无道,止于术
欢迎大家关注Java之道公众号
好文章,我在看❤️
原来使用 Spring 实现策略模式可以这么简单!相关推荐
- 如何使用 Spring 实现策略模式+工厂模式
欢迎关注方志朋的博客,回复"666"获面试宝典 一.策略模式 策略模式定义了一组算法,将每个算法都封装起来,并且使它们之间可以互换 1.策略模式主要角色 主要角色如下: 封装角色( ...
- spring AOP策略模式使用
1.策略模式 The Strategy Pattern defines a family of algorithms,encapsulates each one,and makes them inte ...
- 策略模式及Spring整合策略模式
策略模式 抽象策略类 interface SortService{int[] sort(int arr[]);} 具体策略类 class InsertionSortServiceImpl implem ...
- Spring实现策略模式
通过Spring实现策略模式 当程序中使用太多的if/else/switch来处理不同类型的业务时,会变得极难维护,通过策略模式可以更容易的实现业务扩展和维护. 标准策略模式介绍 比如说对象的某个行为 ...
- Spring 中策略模式的 2 个经典应用
点击蓝色"程序猿DD"关注我哟 加个"星标",不忘签到哦 转自头条号程序汪汪 背景 程序员在项目实战中,策略模式用的非常多. 学习目标 会在Spring项目中运 ...
- Spring 中策略模式的 2 个经典应用,可以用来怼面试官了
一.背景 程序员在项目实战中,策略模式用的非常多. 二.学习目标 会在Spring项目中运用策略模式 三.代码例子 废话不多说,Java的软件开发们注意啦,开车啦! 下面是一个查询业务使用策略模式的案 ...
- Spring中策略模式实现方法
一.定义 在策略模式(Strategy Pattern)中,一个类的行为或其算法可以在运行时更改.这种类型的设计模式属于行为型模式.在策略模式中,我们创建表示各种策略的对象和一个行为随着策略对象改变而 ...
- 策略模式原来这么简单!
前言 只有光头才能变强 回顾前面: 给女朋友讲解什么是代理模式 包装模式就是这么简单啦 单例模式你会几种写法? 工厂模式理解了没有? 无论是面试还是个人的提升,设计模式是必学的.今天来讲解策略模式~ ...
- java spring 实现策略,Spring 环境下实现策略模式的示例
背景 最近在忙一个需求,大致就是给满足特定条件的用户发营销邮件,但是用户的来源有很多方式:从 ES 查询的.从 csv 导入的.从 MongoDB 查询-.. 需求很简单,但是怎么写的优雅,方便后续扩 ...
最新文章
- python消找出img中的src标签_使用beautifulsoup从img标签获取src
- hihoCoder1353 满减优惠
- Flume 1.7 源码分析(三)程序入口
- 8001.win10安装ros2-dashing环境搭建
- Linux之系统操作命令
- sunplus 8202v iop源代码阅读笔记——2
- java栈空异常_Java如何处理空堆栈异常?
- 【Linux开发】【Qt开发】Qt界面键盘、触摸屏、鼠标的响应设置
- Flutter布局常用widgets
- 编译报错:make: *** No rule to make target (例如:starg.h)解决
- TCP 和 UDP 区别及使用场景(详细)
- 当不知轴承型号时如何寻找轴承故障频率_电机轴承的故障诊断与失效分析
- mybatis批量更新报错问题解决
- python解密密文_ctf密码学------密文解码python脚本(凯撒解密)
- java web工程,传递字符串参数小记
- I am a boy!
- C语言入门知识1(零基础新手适用)
- 【剑指Offer】个人学习笔记_15_二进制中1的个数
- Android Camera相机开发示例、Android 开发板 USB摄像头采集、定期拍照、定时拍照,安卓调用摄像头拍照、Android摄像头预览、监控,USB摄像头开发、摄像头监控代码
- 我的人工智能之旅——偏斜类问题
热门文章
- mysql5.7.24 rpm安装_centos7下安装mysql5.7.24
- Ubuntu中的密钥环密码与登陆密码不同
- if laytpl 非_Layui-神奇的layui.laytpl
- QGroupBox详解
- 怎样格式化电脑_硬盘数据销毁最安全的步骤是怎样的?有公司可以做吗
- 计组之数据运算:7、定点数原码除法运算(恢复余数法、加减交替法)
- (软件工程复习核心重点)第四章总体设计-第四节:描绘软件结构的图形工具
- 6-6-1:STL之map和set——set的基本使用
- golang map使用总结
- 使用python将数据导出excel表格