策略模式作为一种软件设计模式,指对象有某个行为,但是在不同的场景中,该行为有不同的实现算法,可以替代代码中大量的 if-else。

比如我们生活中的场景:买东西结账可以使用微信支付、支付宝支付或者银行卡支付,这些交易方式就是不同的策略。

那么在什么时候使用策略模式呢?

在《阿里巴巴Java开发手册》中有提到当超过 3 层的 if-else 的逻辑判断代码可以使用策略模式来实现。

在 Spring 中实现策略模式的方式有很多种,下面通过一个案例来演示下,比如有个需求需要实现支持第三方登录,目前需要支持以下三种登录方式:

  • 微信登录

  • QQ 登录

  • 微博登录

下面将通过策略模式来实现这个需求,其中策略模式结构如下图所示:

策略模式结构如下图所示:

策略模式结构

主要包括一个登录接口类和几种登录方式的实现方式,并利用简单工厂来获取对应的处理器。

定义策略接口

首先定义一个登录的策略接口 LoginHandler,其中包括两个方法:

  1. 获取策略类型的方法

  2. 处理策略逻辑的方法

public interface LoginHandler<T extends Serializable> {/*** 获取登录类型** @return*/LoginType getLoginType();/*** 登录** @param request* @return*/LoginResponse<String, T> handleLogin(LoginRequest request);
}

其中,LoginHandlergetLoginType 方法用来获取登录的类型(即策略类型),用于根据客户端传递的参数直接获取到对应的策略实现。

客户端传递的相关参数都被封装为 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 测下下:

微信登录

QQ登录

是不是很简单呢?如果需求又要加需求,需要支持 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 实现策略模式可以这么简单!相关推荐

  1. 如何使用 Spring 实现策略模式+工厂模式

    欢迎关注方志朋的博客,回复"666"获面试宝典 一.策略模式 策略模式定义了一组算法,将每个算法都封装起来,并且使它们之间可以互换 1.策略模式主要角色 主要角色如下: 封装角色( ...

  2. spring AOP策略模式使用

    1.策略模式 The Strategy Pattern defines a family of algorithms,encapsulates each one,and makes them inte ...

  3. 策略模式及Spring整合策略模式

    策略模式 抽象策略类 interface SortService{int[] sort(int arr[]);} 具体策略类 class InsertionSortServiceImpl implem ...

  4. Spring实现策略模式

    通过Spring实现策略模式 当程序中使用太多的if/else/switch来处理不同类型的业务时,会变得极难维护,通过策略模式可以更容易的实现业务扩展和维护. 标准策略模式介绍 比如说对象的某个行为 ...

  5. Spring 中策略模式的 2 个经典应用

    点击蓝色"程序猿DD"关注我哟 加个"星标",不忘签到哦 转自头条号程序汪汪 背景 程序员在项目实战中,策略模式用的非常多. 学习目标 会在Spring项目中运 ...

  6. Spring 中策略模式的 2 个经典应用,可以用来怼面试官了

    一.背景 程序员在项目实战中,策略模式用的非常多. 二.学习目标 会在Spring项目中运用策略模式 三.代码例子 废话不多说,Java的软件开发们注意啦,开车啦! 下面是一个查询业务使用策略模式的案 ...

  7. Spring中策略模式实现方法

    一.定义 在策略模式(Strategy Pattern)中,一个类的行为或其算法可以在运行时更改.这种类型的设计模式属于行为型模式.在策略模式中,我们创建表示各种策略的对象和一个行为随着策略对象改变而 ...

  8. 策略模式原来这么简单!

    前言 只有光头才能变强 回顾前面: 给女朋友讲解什么是代理模式 包装模式就是这么简单啦 单例模式你会几种写法? 工厂模式理解了没有? 无论是面试还是个人的提升,设计模式是必学的.今天来讲解策略模式~ ...

  9. java spring 实现策略,Spring 环境下实现策略模式的示例

    背景 最近在忙一个需求,大致就是给满足特定条件的用户发营销邮件,但是用户的来源有很多方式:从 ES 查询的.从 csv 导入的.从 MongoDB 查询-.. 需求很简单,但是怎么写的优雅,方便后续扩 ...

最新文章

  1. python消找出img中的src标签_使用beautifulsoup从img标签获取src
  2. hihoCoder1353 满减优惠
  3. Flume 1.7 源码分析(三)程序入口
  4. 8001.win10安装ros2-dashing环境搭建
  5. Linux之系统操作命令
  6. sunplus 8202v iop源代码阅读笔记——2
  7. java栈空异常_Java如何处理空堆栈异常?
  8. 【Linux开发】【Qt开发】Qt界面键盘、触摸屏、鼠标的响应设置
  9. Flutter布局常用widgets
  10. 编译报错:make: *** No rule to make target (例如:starg.h)解决
  11. TCP 和 UDP 区别及使用场景(详细)
  12. 当不知轴承型号时如何寻找轴承故障频率_电机轴承的故障诊断与失效分析
  13. mybatis批量更新报错问题解决
  14. python解密密文_ctf密码学------密文解码python脚本(凯撒解密)
  15. java web工程,传递字符串参数小记
  16. I am a boy!
  17. C语言入门知识1(零基础新手适用)
  18. 【剑指Offer】个人学习笔记_15_二进制中1的个数
  19. Android Camera相机开发示例、Android 开发板 USB摄像头采集、定期拍照、定时拍照,安卓调用摄像头拍照、Android摄像头预览、监控,USB摄像头开发、摄像头监控代码
  20. 我的人工智能之旅——偏斜类问题

热门文章

  1. mysql5.7.24 rpm安装_centos7下安装mysql5.7.24
  2. Ubuntu中的密钥环密码与登陆密码不同
  3. if laytpl 非_Layui-神奇的layui.laytpl
  4. QGroupBox详解
  5. 怎样格式化电脑_硬盘数据销毁最安全的步骤是怎样的?有公司可以做吗
  6. 计组之数据运算:7、定点数原码除法运算(恢复余数法、加减交替法)
  7. (软件工程复习核心重点)第四章总体设计-第四节:描绘软件结构的图形工具
  8. 6-6-1:STL之map和set——set的基本使用
  9. golang map使用总结
  10. 使用python将数据导出excel表格