<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          原來使用 Spring 實現(xiàn)策略模式可以這么簡單!

          共 5788字,需瀏覽 12分鐘

           ·

          2021-02-02 03:42








          策略模式作為一種軟件設(shè)計模式,指對象有某個行為,但是在不同的場景中,該行為有不同的實現(xiàn)算法,可以替代代碼中大量的 if-else。

          比如我們生活中的場景:買東西結(jié)賬可以使用微信支付、支付寶支付或者銀行卡支付,這些交易方式就是不同的策略。

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

          在《阿里巴巴Java開發(fā)手冊》中有提到當(dāng)超過 3 層的 if-else 的邏輯判斷代碼可以使用策略模式來實現(xiàn)。

          在 Spring 中實現(xiàn)策略模式的方式有很多種,下面通過一個案例來演示下,比如有個需求需要實現(xiàn)支持第三方登錄,目前需要支持以下三種登錄方式:

          • 微信登錄
          • QQ 登錄
          • 微博登錄

          下面將通過策略模式來實現(xiàn)這個需求,其中策略模式結(jié)構(gòu)如下圖所示:

          策略模式結(jié)構(gòu)如下圖所示:

          策略模式結(jié)構(gòu)

          主要包括一個登錄接口類和幾種登錄方式的實現(xiàn)方式,并利用簡單工廠來獲取對應(yīng)的處理器。

          定義策略接口

          首先定義一個登錄的策略接口 LoginHandler,其中包括兩個方法:

          1. 獲取策略類型的方法
          2. 處理策略邏輯的方法
          public?interface?LoginHandler<T?extends?Serializable>?{

          ????/**
          ?????*?獲取登錄類型
          ?????*
          ?????*?@return
          ?????*/

          ????LoginType?getLoginType();

          ????/**
          ?????*?登錄
          ?????*
          ?????*?@param?request
          ?????*?@return
          ?????*/

          ????LoginResponse?handleLogin(LoginRequest?request);
          }

          其中,LoginHandlergetLoginType 方法用來獲取登錄的類型(即策略類型),用于根據(jù)客戶端傳遞的參數(shù)直接獲取到對應(yīng)的策略實現(xiàn)。

          客戶端傳遞的相關(guān)參數(shù)都被封裝為 LoginRequest,傳遞給 handleLogin 進(jìn)行處理。

          @Data
          public?class?LoginRequest?{

          ????private?LoginType?loginType;

          ????private?Long?userId;
          }

          其中,根據(jù)需求定義登錄類型枚舉如下:

          public?enum?LoginType?{
          ????QQ,
          ????WE_CHAT,
          ????WEI_BO;
          }

          實現(xiàn)策略接口

          在定義好策略接口后,我們就需要根據(jù)各種第三方登錄來實現(xiàn)對應(yīng)的處理邏輯就可以了。

          微信登錄

          @Component
          public?class?WeChatLoginHandler?implements?LoginHandler<String>?{

          ????private?final?Logger?logger?=?LoggerFactory.getLogger(this.getClass());

          ????/**
          ?????*?獲取登錄類型
          ?????*
          ?????*?@return
          ?????*/

          ????@Override
          ????public?LoginType?getLoginType()?{
          ????????return?LoginType.WE_CHAT;
          ????}

          ????/**
          ?????*?登錄
          ?????*
          ?????*?@param?request
          ?????*?@return
          ?????*/

          ????@Override
          ????public?LoginResponse?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
          ?????*/

          ????@Override
          ????public?LoginType?getLoginType()?{
          ????????return?LoginType.QQ;
          ????}

          ????/**
          ?????*?登錄
          ?????*
          ?????*?@param?request
          ?????*?@return
          ?????*/

          ????@Override
          ????public?LoginResponse?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
          ?????*/

          ????@Override
          ????public?LoginType?getLoginType()?{
          ????????return?LoginType.WEI_BO;
          ????}

          ????/**
          ?????*?登錄
          ?????*
          ?????*?@param?request
          ?????*?@return
          ?????*/

          ????@Override
          ????public?LoginResponse?handleLogin(LoginRequest?request)?{
          ????????logger.info("微博登錄:userId:{}",?request.getUserId());
          ????????return?LoginResponse.success("微博登錄成功",?null);
          ????}
          }

          創(chuàng)建策略的簡單工廠

          @Component
          public?class?LoginHandlerFactory?implements?InitializingBean,?ApplicationContextAware?{
          ????private?static?final?Map>?LOGIN_HANDLER_MAP?=?new?EnumMap<>(LoginType.class);
          ????private?ApplicationContext?appContext;

          ????/**
          ?????*?根據(jù)登錄類型獲取對應(yīng)的處理器
          ?????*
          ?????*?@param?loginType?登錄類型
          ?????*?@return?登錄類型對應(yīng)的處理器
          ?????*/

          ????public?LoginHandler?getHandler(LoginType?loginType)?{
          ????????return?LOGIN_HANDLER_MAP.get(loginType);
          ????}

          ????@Override
          ????public?void?afterPropertiesSet()?throws?Exception?{
          ????????//?將?Spring?容器中所有的?LoginHandler?注冊到?LOGIN_HANDLER_MAP
          ????????appContext.getBeansOfType(LoginHandler.class)
          ????????????????.values()
          ????????????????.forEach(handler?->?LOGIN_HANDLER_MAP.put(handler.getLoginType(),?handler))
          ;
          ????}

          ????@Override
          ????public?void?setApplicationContext(ApplicationContext?applicationContext)?throws?BeansException?{
          ????????appContext?=?applicationContext;
          ????}
          }

          我們讓 LoginHandlerFactory實現(xiàn) InitializingBean 接口,在 afterPropertiesSet 方法中,基于 Spring 容器將所有 LoginHandler 自動注冊到 LOGIN_HANDLER_MAP,從而 Spring 容器啟動完成后, getHandler 方法可以直接通過 loginType 來獲取對應(yīng)的登錄處理器。

          創(chuàng)建登錄服務(wù)

          在登錄服務(wù)中,我們通過 LoginHandlerFactory 來獲取對應(yīng)的登錄處理器,從而處理不同類型的第三方登錄:

          @Service
          public?class?LoginServiceImpl?implements?LoginService?{
          ????@Autowired
          ????private?LoginHandlerFactory?loginHandlerFactory;

          ????@Override
          ????public?LoginResponse?login(LoginRequest?request)?{
          ????????LoginType?loginType?=?request.getLoginType();
          ????????//?根據(jù)?loginType?找到對應(yīng)的登錄處理器
          ????????LoginHandler?loginHandler?=
          ????????????????loginHandlerFactory.getHandler(loginType);
          ????????//?處理登錄
          ????????return?loginHandler.handleLogin(request);
          ????}
          }

          Factory 只負(fù)責(zé)獲取 Handler,Handler 只負(fù)責(zé)處理具體的登錄,Service 只負(fù)責(zé)邏輯編排,從而達(dá)到功能上的低耦合高內(nèi)聚。

          測試

          寫一個 Controller:

          @RestController
          public?class?LoginController?{

          ????@Autowired
          ????private?LoginService?loginService;

          ????/**
          ?????*?登錄
          ?????*/

          ????@PostMapping("/login")
          ????public?LoginResponse?login(@RequestParam?LoginType?loginType,?@RequestParam?Long?userId)?{
          ????????LoginRequest?loginRequest?=?new?LoginRequest();
          ????????loginRequest.setLoginType(loginType);
          ????????loginRequest.setUserId(userId);
          ????????return?loginService.login(loginRequest);
          ????}
          }

          然后用 Postman 測下下:

          微信登錄
          QQ登錄

          是不是很簡單呢?如果需求又要加需求,需要支持 GitHub 第三方登錄。

          此時我們只需要添加一個新的策略實現(xiàn),然后在登錄枚舉中加入對應(yīng)的類型即可:

          @Component
          public?class?GitHubLoginHandler?implements?LoginHandler?{

          ????private?final?Logger?logger?=?LoggerFactory.getLogger(this.getClass());

          ????/**
          ?????*?獲取登錄類型
          ?????*
          ?????*?@return
          ?????*/
          ????@Override
          ????public?LoginType?getLoginType()?{
          ????????return?LoginType.GIT_HUB;
          ????}

          ????/**
          ?????*?登錄
          ?????*
          ?????*?@param?request
          ?????*?@return
          ?????*/
          ????@Override
          ????public?LoginResponse?handleLogin(LoginRequest?request)?{
          ????????logger.info("GitHub登錄:userId:{}",?request.getUserId());
          ????????return?LoginResponse.success("GitHub登錄成功",?null);
          ????}
          }

          此時不需要修改任何代碼 ,因為 Spring 容器重啟時會自動將 GitHubLoginHandler 注冊到 LoginHandlerFactory 中,使用 Spring 實現(xiàn)策略模式就是這么簡單,還不快學(xué)起來!


          ? ? ? ?
          ???
          說完觀察者和發(fā)布訂閱模式的區(qū)別,面試官不留我吃飯了
          模板方法模式——看看 JDK 和 Spring 是如何優(yōu)雅復(fù)用代碼的
          從原型模式到淺拷貝和深拷貝

          覺得不錯,點個在看~

          瀏覽 55
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  额去撸在线 | 一本色道综合久久欧美日韩精品 | 在线观看色天堂 | 亚洲av网站在线观看 | 日本处女学生妺一级片 |