当前位置: 首页 > 科技观察

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

时间:2023-03-21 11:54:33 科技观察

本文转载自微信公众号“吴佩轩”,作者吴佩轩。转载本文请联系吴佩萱公众号。作为一种软件设计模式,策略模式是指一个对象具有一定的行为,但在不同的场景下,该行为有不同的实现算法,可以替代代码中大量的if-else。比如我们生活中的场景:可以使用微信支付、支付宝支付或者银行卡支付进行购物结账。这些交易方式是不同的策略。那么什么时候使用策略模式呢?《阿里巴巴Java开发手册》中提到,3层以上的if-else的逻辑判断代码可以使用策略模式来实现。Spring中策略模式的实现方式有很多种。这是一个案例来证明它。比如需要支持第三方登录。目前需要支持以下三种登录方式:微信登录QQ登录微博登录下面会通过策略模式来实现这个需求,策略模式的结构如下图所示:策略模式如下图所示:策略模式的结构主要包括一个登录接口类和几个登录方法的实现,并使用一个简单的工厂来获取对应的处理器。定义策略接口首先定义一个登录策略接口LoginHandler,包含两个方法:获取策略类型的方法和处理策略逻辑的方法publicinterfaceLoginHandler{/***获取登录类型**@return*/登录类型getLoginType();/***Login**@paramrequest*@return*/LoginResponsehandleLogin(LoginRequestrequest);}其中,LoginHandler的getLoginType方法用于获取登录类型(即策略类型),用于根据客户端传递的参数直接获取到相应的策略实现。将客户端传递过来的相关参数封装为LoginRequest,传递给handleLogin处理。@DatapublicclassLoginRequest{privateLoginTypeloginType;privateLonguserId;}其中,根据需求定义的登录类型枚举如下:publicenumLoginType{QQ,WE_CHAT,WEI_BO;}实现策略接口定义策略接口后,我们需要根据对应实现对各种第三方登录的处理逻辑没问题。微信登录@ComponentpublicclassWeChatLoginHandlerimplementsLoginHandler{privatefinalLoggerlogger=LoggerFactory.getLogger(this.getClass());/***获取登录类型**@return*/@OverridepublicLoginTypegetLoginType(){returnLoginType.WE_CHAT;}/***login**@paramrequest*@return*/@OverridepublicLoginResponsehandleLogin(LoginRequestrequest){logger.info("微信登录:userId:{}",request.getUserId());StringweChatName=getWeChatName(request);returnLoginResponse.success("微信登录成功",weChatName);}privateStringgetWeChatName(LoginRequestrequest){return"wupx";}}QQ登录@ComponentpublicclassQQLoginHandlerimplementsLoginHandler{privatefinalLoggerlogger=LoggerFactory.getLogger(this.getClass());/***获取登录类型**@return*/@OverridepublicLoginTypegetLoginType(){returnLoginType.QQ;}/***login**@paramrequest*@return*/@OverridepublicLoginResponsehandleLogin(LoginRequestrequest){logger.info("QQ登录:userId:{}",request.getUserId());returnLoginResponse.success("QQ登录成功",null);}}微博登录@ComponentpublicclassWeiBoLoginHandlerimplementsLoginHandler{privatefinalLoggerlogger=LoggerFactory.getLogger(this.getClass());/***获取登录类型**@return*/@OverridepublicLoginTypegetLoginType(){returnLoginType.WEI_BO;}/***login**@paramrequest*@return*/@OverridepublicLoginResponsehandleLogin(LoginRequestrequest){logger.info("微博登录:userId:{}",request.getUserId());returnLoginResponse.success("微博登录成功",null);}}创建策略简单工厂@ComponentpublicclassLoginHandlerFactoryimplementsInitializingBean,ApplicationContextAware{privatestaticfinalMap>LOGIN_HANDLER_MAP=newEnumMap<>(LoginType.class);privateApplicationContextappContext;/***根据登录类型获取对应的handler**@paramloginType登录类型*@return登录类型对应的handler*/publicLoginHandlergetHandler(LoginTypeloginType){returnLOGIN_HANDLER_MAP.get(loginType);}@OverridepublicvoidafterPropertiesSet()throwsException{//将Spring容器中的所有LoginHandler注册到LOGIN_HANDLER_MAPappContext.getBeansOfType(LoginHandler.class).values().forEach(handler->LOGIN_HANDLER_MAP.put(handler.getLoginType(),handler));}@OverridepublicvoidsetApplicationContext(ApplicationContextapplicationContext)throwsBeansException{appContext=applicationContext;}}我们让LoginHandlerFactory实现InitializingBean接口。在afterPropertiesSet方法中,所有的LoginHandlers都会自动注册到基于Spring容器的LOGIN_HANDLER_MAP中,这样在Spring容器启动后,getHandler方法可以直接通过loginType获取对应的登录handler创建登录服务在登录服务中,我们通过LoginHandlerFactory获取对应的登录handler来处理不同类型的第三方登录:.getLoginType();//根据loginTypeLoginHandlerloginHandler=loginHandlerFactory.getHandler(loginType);//处理登录returnloginHandler.handleLogin(request);}}Factory只负责获取Handler,而Handler只负责处理具体的登录,Service只负责逻辑安排,在功能上做到低耦合高内聚。测试编写控制器:@RestControllerpublicclassLoginController{@AutowiredprivateLoginServiceloginService;/***login*/@PostMapping("/login")publicLoginResponselogin(@RequestParamLoginTypeloginType,@RequestParamLonguserId){LoginRequestloginRequest=newLoginRequest();loginTypeLoginRequest.set(loginType);loginRequest.setUserId(userId);returnloginService.login(loginRequest);}}然后用Postman测试:微信登录QQ登录是不是很简单?如果需要添加需求,需要支持GitHub第三方登录。此时我们只需要添加一个新的策略实现,然后在登录枚举中添加对应的类型:@ComponentpublicclassGitHubLoginHandlerimplementsLoginHandler{privatefinalLoggerlogger=LoggerFactory.getLogger(this.getClass());/***获取登录输入**@return*/@OverridepublicLoginTypegetLoginType(){returnLoginType.GIT_HUB;}/***login**@paramrequest*@return*/@OverridepublicLoginResponsehandleLogin(LoginRequestrequest){logger.info("GitHub登录:userId:{}",request.getUserId());returnLoginResponse.success("GitHubloginsuccessful",null);}}这时候不需要修改任何代码,因为Spring容器会自动将GitHubLoginHandler注册到LoginHandlerFactory中重启吧,用Spring实现策略模式就是这么简单,学起来也不容易啊!