javascript
原来使用 Spring 实现策略模式可以这么简单!
策略模式作為一種軟件設計模式,指對象有某個行為,但是在不同的場景中,該行為有不同的實現算法,可以替代代碼中大量的 if-else。
比如我們生活中的場景:買東西結賬可以使用微信支付、支付寶支付或者銀行卡支付,這些交易方式就是不同的策略。
那么在什么時候使用策略模式呢?
在《阿里巴巴Java開發手冊》中有提到當超過 3 層的 if-else 的邏輯判斷代碼可以使用策略模式來實現。
在 Spring 中實現策略模式的方式有很多種,下面通過一個案例來演示下,比如有個需求需要實現支持第三方登錄,目前需要支持以下三種登錄方式:
微信登錄
QQ 登錄
微博登錄
下面將通過策略模式來實現這個需求,其中策略模式結構如下圖所示:
策略模式結構如下圖所示:
策略模式結構主要包括一個登錄接口類和幾種登錄方式的實現方式,并利用簡單工廠來獲取對應的處理器。
?
定義策略接口
首先定義一個登錄的策略接口 LoginHandler,其中包括兩個方法:
獲取策略類型的方法
處理策略邏輯的方法
其中,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 實現策略模式就是這么簡單,還不快學起來!
有道無術,術可成;有術無道,止于術
歡迎大家關注Java之道公眾號
好文章,我在看??
總結
以上是生活随笔為你收集整理的原来使用 Spring 实现策略模式可以这么简单!的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: ASP.NET页面刷新的几种实现方法
- 下一篇: 小圈子.大社交:利用圈子引爆流行