原来使用 Spring 实现策略模式可以这么简单

article/2025/10/4 12:53:10

策略模式作为一种软件设计模式,指对象有某个行为,但是在不同的场景中,该行为有不同的实现算法,可以替代代码中大量的 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);
}

其中,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 测下下:

微信登录

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 实现策略模式就是这么简单,还不快学起来!


http://chatgpt.dhexx.cn/article/v56jFLQF.shtml

相关文章

策略设计模式

介绍 Java策略模式(Strategy Pattern)是一种行为设计模式,它允许再运行时动态选择算法的行为.策略模式通过将算法封装在可互换的策略对象中,使得客户端代码能够轻松地切换算法,而无需修改原始代码.在策略模式中,每个算法都被实现为一个单独的策略类,这些策略类都实现了相同的接…

Java 设计模式——策略模式(行为型设计模式)

策略模式用我个人的理解就是&#xff1a; 用一种更灵活更优雅和更健壮的方式替换了if…else… 一、问题引入 当我们导出一些数据到Excel表格时&#xff0c;有时候需要从不同的维度导出&#xff08;如&#xff1a;个人维度&#xff0c;时间维度&#xff09;&#xff0c;不同维…

采用注解实现策略模式

目录 一、前言 二、采用简单的注解方式进行业务策略模式 &#xff08;一&#xff09;场景举例 &#xff08;二&#xff09;实现方案 1、基本代码准备 2、基本功能接口定义 3、定义注解与不同的策略实现 4、业务实际使用 5、测试及结果展示 三、采用组合的注解方式进行…

​JAVA设计模式(六)——策略模式

下图为所有设计模式&#xff0c;带标记为重点掌握以及工作中常用到的&#xff1a; 策略模式是行为型设计模式之一&#xff0c;其作用是让一个类的行为或其算法可以在运行时更改&#xff0c;该模式也算是我比较熟悉的模式之一了&#xff0c;因为之前项目中有幸遇到大佬用过&…

java 策略模式例子_策略模式—Java实现(转)

1. 现实需求 客户有了新的需求&#xff0c;这时我们直接新增策略即可&#xff0c;改很少的代码。基本符合我们面向对象原则中的开闭原则(对扩展开放&#xff0c;对修改关系)&#xff0c;实现了高内聚低耦合。 2. 策略模式定义 策略模式&#xff0c;又叫算法簇模式&#xff0c;就…

Java设计模式(五)策略模式-在SpringBoot项目中的实际应用

文章目录 什么是策略模式优点缺点使用场景结构图 策略模式的简单示例策略模式的项目实战场景实现 小结 什么是策略模式 官话&#xff1a;策略模式(Strategy Pattern)&#xff1a; 定义一系列算法类&#xff0c;将每一个算法封装起来&#xff0c;并让它们可以相互替换&#xff…

java调用微信加密_java微信消息加解密

今天心血来潮就信手拈来学了下微信消息加解密的知识,忽然觉得微信真的好强大。可能在大部分项目微信消息的加解密都用不上,但是仍然不排除有使用到的情况,如涉及金钱方面的微信应用包括商城类、金融类还有其他安全级别要求很高的微信应用。针对这些情况我觉得还是有必要分享…

spring如何使用策略模式

这里使用登录做例子介绍如何实现登录的多种策略 上图是策略模式的基础模型。 context Context上下文角色&#xff0c;也叫Context封装角色&#xff0c;起承上启下的作用&#xff0c;屏蔽高层模块对策略、算法的直接访问&#xff0c;封装可能存在的变化。 Strategy 策略角色…

java设计模式实战-(反射+策略模式)

学完23种设计模式&#xff0c;相信很多同学都疑问&#xff0c;除了单例模式、工厂模式其他模式还有运用的场景吗&#xff1f; 现在这里就举一个例子。 首先策略模式需要先有了解&#xff0c;我们常用策略模式解决实际开发中的if else特别多的场景。但是在实际的开发中&#x…

Java23种设计模式之策略模式【普通写法以及spring中的写法】

目录 设计模式简介策略模式的简介普通写法案例基于注解式改造的案例优缺点策略模式的使用场景 设计模式简介 将设计者的思维融入大家的学习和工作中&#xff0c;更高层次的思考&#xff01; • 创建型模式&#xff1a; – 单例模式、工厂模式、抽象工厂模式、建造者模式、原型…

Java设计模式-策略模式(支付业务的优化)

一、什么情况下使用策略模式 功能类似的业务功能&#xff0c;避免创建多个接口&#xff0c;大量if else 判断逻辑&#xff0c;简化代码 二、我以支付为例&#xff08;支付方式有微信&#xff0c;支付宝支付&#xff09; 1、新建策略抽象类或者接口 public interface ThirdP…

java策略模式实战示例

现已放在gitee上,可以不下载直接参考一下即: https://gitee.com/zhang-xiao-xiang/zxx-pattern 日常碰到的业务概述 登录类型,支付类型,供应商渠道,不同等级会员享受的优惠券价格不一样,等等业务判断,大量if else导致拓展(侧重新增)极其困难,维护(侧重修改)自然是改起来头痛(…

【JAVA设计模式】策略模式

1.什么是策略模式&#xff1f; 策略模式是对算法的包装&#xff0c;是把使用算法的责任和算法本身分割开来&#xff0c;委派给不同的对象管理&#xff0c;相同的事情-----选择不用同方式 &#xff08;不同实现&#xff09;举例子&#xff0c;最终可以实现解决多重if判断问题。 …

java微信关注事件_java策略模式在接收微信事件推送上的具体应用

java策略模式&#xff0c;在我的认知中是根据不同选择执行不同的实现。通过if或者switch-case也能实现这种逻辑&#xff0c;但是代码冗余&#xff0c;可扩展性不强。 百度百科上的解释为策略模式作为一种软件设计模式&#xff0c;指对象有某个行为&#xff0c;但是在不同的场景…

Java设计模式之3种策略模式实现

一、什么是策略模式 所谓策略模式&#xff0c;就是定义了一组策略&#xff0c;分别封装在不同类中&#xff0c;每种策略都可以根据当前场景相互替换&#xff0c;从而使策略的变化可以独立于操作者。比如我们要去某个地方&#xff0c;会根据距离的不同来选择不同的出行方式&…

java8 策略模式_JAVA设计模式之策略模式

策略模式&#xff1a;在策略模式(Strategy Pattern)中&#xff0c;一个类的行为或其算法可以在运行时更改。这种类型的设计模式属于行为型模式。 在策略模式中&#xff0c;我们创建表示各种策略的对象和一个行为随着策略对象改变而改变的 context 对象。策略对象改变 context 对…

git如何提交代码

代码提交 代码提交一般有五个步骤&#xff1a; 1.查看目前代码的修改状态 2.查看代码修改内容 3.暂存需要提交的文件 4.提交已暂存的文件 5.同步到服务器 1. 查看目前代码的修改状态 提交代码之前&#xff0c;首先应该检查目前所做的修改&#xff0c;运行git status命令 a)…

修改git提交时间

问题描述&#xff1a; 修改git提交记录的时间 git脚本&#xff1a; 此方法使用github上的开源工具完成。感谢无私的奉献者&#xff01; 操作步骤&#xff1a; github上下载文件 解压文件夹&#xff0c;把git-redate文件置于git安装目录的\mingw64\libexec\git-core文件夹…

git提交时常用命令

git提交时常用命令 一、git命令 -- 本地仓库与远程仓库建立链接 git init &#xff08;初始化本地仓库&#xff09; git remote add origin 远程仓库网址 git remote -v (查看远程仓库) git remote rm origin (删除远程分支)-- checkout多种用法 git checkout 文件名 &#…

git提交分支

1. git提交分支相关 在本地新建分支&#xff0c;保证和远程分支一样 git checkout -b 分支名如果分支已存在&#xff0c;只需要切换的话 git checkout 分支名提交前先把代码拉下来更新一下&#xff0c;确保不会覆盖别人的代码 git pull origin 远程分支(如果有)解决冲突 git …