<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>

          代碼精簡10倍,責(zé)任鏈模式y(tǒng)yds

          共 12040字,需瀏覽 25分鐘

           ·

          2023-05-15 14:21

          目錄

          • 背景
          • 什么是責(zé)任鏈
          • 使用場景
          • 結(jié)語

          背景

          最近,我讓團(tuán)隊(duì)內(nèi)一位成員寫了一個(gè)導(dǎo)入功能。他使用了責(zé)任鏈模式,代碼堆的非常多,bug 也多,沒有達(dá)到我預(yù)期的效果。

          實(shí)際上,針對導(dǎo)入功能,我認(rèn)為模版方法更合適!為此,隔壁團(tuán)隊(duì)也拿出我們的案例,進(jìn)行了集體 code review。

          學(xué)好設(shè)計(jì)模式,且不要為了練習(xí),強(qiáng)行使用!讓原本 100 行就能實(shí)現(xiàn)的功能,寫了 3000 行!對錯(cuò)暫且不論,我們先一起看看責(zé)任鏈設(shè)計(jì)模式吧!

          什么是責(zé)任鏈

          責(zé)任鏈模式是一種行為設(shè)計(jì)模式, 允許你將請求沿著處理者鏈進(jìn)行發(fā)送。收到請求后, 每個(gè)處理者均可對請求進(jìn)行處理, 或?qū)⑵鋫鬟f給鏈上的下個(gè)處理者。

          a0e8fa80309d69881f606fb5e02c4676.webp圖片

          使用場景

          責(zé)任鏈的使用場景還是比較多的:

          • 多條件流程判斷:權(quán)限控制

          • ERP 系統(tǒng)流程審批:總經(jīng)理、人事經(jīng)理、項(xiàng)目經(jīng)理

          • Java 過濾器的底層實(shí)現(xiàn) Filter

          如果不使用該設(shè)計(jì)模式,那么當(dāng)需求有所改變時(shí),就會(huì)使得代碼臃腫或者難以維護(hù),例如下面的例子。

          | 反例

          假設(shè)現(xiàn)在有一個(gè)闖關(guān)游戲,進(jìn)入下一關(guān)的條件是上一關(guān)的分?jǐn)?shù)要高于 xx:

          • 游戲一共 3 個(gè)關(guān)卡

          • 進(jìn)入第二關(guān)需要第一關(guān)的游戲得分大于等于 80

          • 進(jìn)入第三關(guān)需要第二關(guān)的游戲得分大于等于 90

          那么代碼可以這樣寫:

              //第一關(guān)??
          public?class?FirstPassHandler?{??
          ????public?int?handler(){??
          ????????System.out.println("第一關(guān)-->FirstPassHandler");??
          ????????return?80;??
          ????}??
          }??
          ??
          //第二關(guān)??
          public?class?SecondPassHandler?{??
          ????public?int?handler(){??
          ????????System.out.println("第二關(guān)-->SecondPassHandler");??
          ????????return?90;??
          ????}??
          }??
          ??
          ??
          //第三關(guān)??
          public?class?ThirdPassHandler?{??
          ????public?int?handler(){??
          ????????System.out.println("第三關(guān)-->ThirdPassHandler,這是最后一關(guān)啦");??
          ????????return?95;??
          ????}??
          }??
          ??
          ??
          //客戶端??
          public?class?HandlerClient?{??
          ????public?static?void?main(String[]?args)?{??
          ??
          ????????FirstPassHandler?firstPassHandler?=?new?FirstPassHandler();//第一關(guān)??
          ????????SecondPassHandler?secondPassHandler?=?new?SecondPassHandler();//第二關(guān)??
          ????????ThirdPassHandler?thirdPassHandler?=?new?ThirdPassHandler();//第三關(guān)??
          ??
          ????????int?firstScore?=?firstPassHandler.handler();??
          ????????//第一關(guān)的分?jǐn)?shù)大于等于80則進(jìn)入第二關(guān)??
          ????????if(firstScore?>=?80){??
          ????????????int?secondScore?=?secondPassHandler.handler();??
          ????????????//第二關(guān)的分?jǐn)?shù)大于等于90則進(jìn)入第二關(guān)??
          ????????????if(secondScore?>=?90){??
          ????????????????thirdPassHandler.handler();??
          ????????????}??
          ????????}??
          ????}??
          }??

          那么如果這個(gè)游戲有 100 關(guān),我們的代碼很可能就會(huì)寫成這個(gè)樣子:

              if(第1關(guān)通過){??
          ????//?第2關(guān)?游戲??
          ????if(第2關(guān)通過){??
          ????????//?第3關(guān)?游戲??
          ????????if(第3關(guān)通過){??
          ???????????//?第4關(guān)?游戲??
          ????????????if(第4關(guān)通過){??
          ????????????????//?第5關(guān)?游戲??
          ????????????????if(第5關(guān)通過){??
          ????????????????????//?第6關(guān)?游戲??
          ????????????????????if(第6關(guān)通過){??
          ????????????????????????//...??
          ????????????????????}??
          ????????????????}??
          ????????????}???
          ????????}??
          ????}??
          }??

          這種代碼不僅冗余,并且當(dāng)我們要將某兩關(guān)進(jìn)行調(diào)整時(shí)會(huì)對代碼非常大的改動(dòng),這種操作的風(fēng)險(xiǎn)是很高的,因此,該寫法非常糟糕。

          | 初步改造

          如何解決這個(gè)問題,我們可以通過鏈表將每一關(guān)連接起來,形成責(zé)任鏈的方式,第一關(guān)通過后是第二關(guān),第二關(guān)通過后是第三關(guān)....

          這樣客戶端就不需要進(jìn)行多重 if 的判斷了:

              public?class?FirstPassHandler?{??
          ????/**??
          ?????*?第一關(guān)的下一關(guān)是?第二關(guān)??
          ?????*/
          ??
          ????private?SecondPassHandler?secondPassHandler;??
          ??
          ????public?void?setSecondPassHandler(SecondPassHandler?secondPassHandler)?{??
          ????????this.secondPassHandler?=?secondPassHandler;??
          ????}??
          ??
          ????//本關(guān)卡游戲得分??
          ????private?int?play(){??
          ????????return?80;??
          ????}??
          ??
          ????public?int?handler(){??
          ????????System.out.println("第一關(guān)-->FirstPassHandler");??
          ????????if(play()?>=?80){??
          ????????????//分?jǐn)?shù)>=80?并且存在下一關(guān)才進(jìn)入下一關(guān)??
          ????????????if(this.secondPassHandler?!=?null){??
          ????????????????return?this.secondPassHandler.handler();??
          ????????????}??
          ????????}??
          ??
          ????????return?80;??
          ????}??
          }??
          ??
          public?class?SecondPassHandler?{??
          ??
          ????/**??
          ?????*?第二關(guān)的下一關(guān)是?第三關(guān)??
          ?????*/
          ??
          ????private?ThirdPassHandler?thirdPassHandler;??
          ??
          ????public?void?setThirdPassHandler(ThirdPassHandler?thirdPassHandler)?{??
          ????????this.thirdPassHandler?=?thirdPassHandler;??
          ????}??
          ??
          ????//本關(guān)卡游戲得分??
          ????private?int?play(){??
          ????????return?90;??
          ????}??
          ??
          ????public?int?handler(){??
          ????????System.out.println("第二關(guān)-->SecondPassHandler");??
          ??
          ????????if(play()?>=?90){??
          ????????????//分?jǐn)?shù)>=90?并且存在下一關(guān)才進(jìn)入下一關(guān)??
          ????????????if(this.thirdPassHandler?!=?null){??
          ????????????????return?this.thirdPassHandler.handler();??
          ????????????}??
          ????????}??
          ??
          ????????return?90;??
          ????}??
          }??
          ??
          public?class?ThirdPassHandler?{??
          ??
          ????//本關(guān)卡游戲得分??
          ????private?int?play(){??
          ????????return?95;??
          ????}??
          ??
          ????/**??
          ?????*?這是最后一關(guān),因此沒有下一關(guān)??
          ?????*/
          ??
          ????public?int?handler(){??
          ????????System.out.println("第三關(guān)-->ThirdPassHandler,這是最后一關(guān)啦");??
          ????????return?play();??
          ????}??
          }??
          ??
          public?class?HandlerClient?{??
          ????public?static?void?main(String[]?args)?{??
          ??
          ????????FirstPassHandler?firstPassHandler?=?new?FirstPassHandler();//第一關(guān)??
          ????????SecondPassHandler?secondPassHandler?=?new?SecondPassHandler();//第二關(guān)??
          ????????ThirdPassHandler?thirdPassHandler?=?new?ThirdPassHandler();//第三關(guān)??
          ??
          ????????firstPassHandler.setSecondPassHandler(secondPassHandler);//第一關(guān)的下一關(guān)是第二關(guān)??
          ????????secondPassHandler.setThirdPassHandler(thirdPassHandler);//第二關(guān)的下一關(guān)是第三關(guān)??
          ??
          ????????//說明:因?yàn)榈谌P(guān)是最后一關(guān),因此沒有下一關(guān)??
          ????????//開始調(diào)用第一關(guān)?每一個(gè)關(guān)卡是否進(jìn)入下一關(guān)卡?在每個(gè)關(guān)卡中判斷??
          ????????firstPassHandler.handler();??
          ??
          ????}??
          }??

          | 缺點(diǎn)

          現(xiàn)有模式的缺點(diǎn):

          • 每個(gè)關(guān)卡中都有下一關(guān)的成員變量并且是不一樣的,形成鏈很不方便

          • 代碼的擴(kuò)展性非常不好

          | 責(zé)任鏈改造

          既然每個(gè)關(guān)卡中都有下一關(guān)的成員變量并且是不一樣的,那么我們可以在關(guān)卡上抽象出一個(gè)父類或者接口,然后每個(gè)具體的關(guān)卡去繼承或者實(shí)現(xiàn)。

          有了思路,我們先來簡單介紹一下責(zé)任鏈設(shè)計(jì)模式的基本組成:

          • 抽象處理者(Handler)角色:?定義一個(gè)處理請求的接口,包含抽象處理方法和一個(gè)后繼連接。

          • 具體處理者(Concrete Handler)角色:?實(shí)現(xiàn)抽象處理者的處理方法,判斷能否處理本次請求,如果可以處理請求則處理,否則將該請求轉(zhuǎn)給它的后繼者。

          • 客戶類(Client)角色:?創(chuàng)建處理鏈,并向鏈頭的具體處理者對象提交請求,它不關(guān)心處理細(xì)節(jié)和請求的傳遞過程。

          16918d5d0ab2c1204f7d90732ad40c79.webp圖片
              public?abstract?class?AbstractHandler?{??
          ??
          ????/**??
          ?????*?下一關(guān)用當(dāng)前抽象類來接收??
          ?????*/
          ??
          ????protected?AbstractHandler?next;??
          ??
          ????public?void?setNext(AbstractHandler?next)?{??
          ????????this.next?=?next;??
          ????}??
          ??
          ????public?abstract?int?handler();??
          }??
          ??
          public?class?FirstPassHandler?extends?AbstractHandler{??
          ??
          ????private?int?play(){??
          ????????return?80;??
          ????}??
          ??
          ????@Override??
          ????public?int?handler(){??
          ????????System.out.println("第一關(guān)-->FirstPassHandler");??
          ????????int?score?=?play();??
          ????????if(score?>=?80){??
          ????????????//分?jǐn)?shù)>=80?并且存在下一關(guān)才進(jìn)入下一關(guān)??
          ????????????if(this.next?!=?null){??
          ????????????????return?this.next.handler();??
          ????????????}??
          ????????}??
          ????????return?score;??
          ????}??
          }??
          ??
          public?class?SecondPassHandler?extends?AbstractHandler{??
          ??
          ????private?int?play(){??
          ????????return?90;??
          ????}??
          ??
          ????public?int?handler(){??
          ????????System.out.println("第二關(guān)-->SecondPassHandler");??
          ??
          ????????int?score?=?play();??
          ????????if(score?>=?90){??
          ????????????//分?jǐn)?shù)>=90?并且存在下一關(guān)才進(jìn)入下一關(guān)??
          ????????????if(this.next?!=?null){??
          ????????????????return?this.next.handler();??
          ????????????}??
          ????????}??
          ??
          ????????return?score;??
          ????}??
          }??
          ??
          public?class?ThirdPassHandler?extends?AbstractHandler{??
          ??
          ????private?int?play(){??
          ????????return?95;??
          ????}??
          ??
          ????public?int?handler(){??
          ????????System.out.println("第三關(guān)-->ThirdPassHandler");??
          ????????int?score?=?play();??
          ????????if(score?>=?95){??
          ????????????//分?jǐn)?shù)>=95?并且存在下一關(guān)才進(jìn)入下一關(guān)??
          ????????????if(this.next?!=?null){??
          ????????????????return?this.next.handler();??
          ????????????}??
          ????????}??
          ????????return?score;??
          ????}??
          }??
          ??
          public?class?HandlerClient?{??
          ????public?static?void?main(String[]?args)?{??
          ??
          ????????FirstPassHandler?firstPassHandler?=?new?FirstPassHandler();//第一關(guān)??
          ????????SecondPassHandler?secondPassHandler?=?new?SecondPassHandler();//第二關(guān)??
          ????????ThirdPassHandler?thirdPassHandler?=?new?ThirdPassHandler();//第三關(guān)??
          ??
          ????????//?和上面沒有更改的客戶端代碼相比,只有這里的set方法發(fā)生變化,其他都是一樣的??
          ????????firstPassHandler.setNext(secondPassHandler);//第一關(guān)的下一關(guān)是第二關(guān)??
          ????????secondPassHandler.setNext(thirdPassHandler);//第二關(guān)的下一關(guān)是第三關(guān)??
          ??
          ????????//說明:因?yàn)榈谌P(guān)是最后一關(guān),因此沒有下一關(guān)??
          ??
          ????????//從第一個(gè)關(guān)卡開始??
          ????????firstPassHandler.handler();??
          ??
          ????}??
          }??

          | 責(zé)任鏈工廠改造

          對于上面的請求鏈,我們也可以把這個(gè)關(guān)系維護(hù)到配置文件中或者一個(gè)枚舉中。我將使用枚舉來教會(huì)大家怎么動(dòng)態(tài)的配置請求鏈并且將每個(gè)請求者形成一條調(diào)用鏈。

          微信搜索公眾號:Java項(xiàng)目精選,回復(fù):java 領(lǐng)取資料 。

          cc2b07c71a8a04c4bf3cd4c1ff01c986.webp圖片
              public?enum?GatewayEnum?{??
          ????//?handlerId,?攔截者名稱,全限定類名,preHandlerId,nextHandlerId??
          ????API_HANDLER(new?GatewayEntity(1,?"api接口限流",?"cn.dgut.design.chain_of_responsibility.GateWay.impl.ApiLimitGatewayHandler",?null,?2)),??
          ????BLACKLIST_HANDLER(new?GatewayEntity(2,?"黑名單攔截",?"cn.dgut.design.chain_of_responsibility.GateWay.impl.BlacklistGatewayHandler",?1,?3)),??
          ????SESSION_HANDLER(new?GatewayEntity(3,?"用戶會(huì)話攔截",?"cn.dgut.design.chain_of_responsibility.GateWay.impl.SessionGatewayHandler",?2,?null)),??
          ????;??
          ??
          ????GatewayEntity?gatewayEntity;??
          ??
          ????public?GatewayEntity?getGatewayEntity()?{??
          ????????return?gatewayEntity;??
          ????}??
          ??
          ????GatewayEnum(GatewayEntity?gatewayEntity)?{??
          ????????this.gatewayEntity?=?gatewayEntity;??
          ????}??
          }??
          ??
          public?class?GatewayEntity?{??
          ??
          ????private?String?name;??
          ??
          ????private?String?conference;??
          ??
          ????private?Integer?handlerId;??
          ??
          ????private?Integer?preHandlerId;??
          ??
          ????private?Integer?nextHandlerId;??
          }??
          ??
          ??
          public?interface?GatewayDao?{??
          ??
          ????/**??
          ?????*?根據(jù)?handlerId?獲取配置項(xiàng)??
          ?????*?@param?handlerId??
          ?????*?@return??
          ?????*/
          ??
          ????GatewayEntity?getGatewayEntity(Integer?handlerId);??
          ??
          ????/**??
          ?????*?獲取第一個(gè)處理者??
          ?????*?@return??
          ?????*/
          ??
          ????GatewayEntity?getFirstGatewayEntity();??
          }??
          ??
          public?class?GatewayImpl?implements?GatewayDao?{??
          ??
          ????/**??
          ?????*?初始化,將枚舉中配置的handler初始化到map中,方便獲取??
          ?????*/
          ??
          ????private?static?Map<Integer,?GatewayEntity>?gatewayEntityMap?=?new?HashMap<>();??
          ??
          ????static?{??
          ????????GatewayEnum[]?values?=?GatewayEnum.values();??
          ????????for?(GatewayEnum?value?:?values)?{??
          ????????????GatewayEntity?gatewayEntity?=?value.getGatewayEntity();??
          ????????????gatewayEntityMap.put(gatewayEntity.getHandlerId(),?gatewayEntity);??
          ????????}??
          ????}??
          ??
          ????@Override??
          ????public?GatewayEntity?getGatewayEntity(Integer?handlerId)?{??
          ????????return?gatewayEntityMap.get(handlerId);??
          ????}??
          ??
          ????@Override??
          ????public?GatewayEntity?getFirstGatewayEntity()?{??
          ????????for?(Map.Entry<Integer,?GatewayEntity>?entry?:?gatewayEntityMap.entrySet())?{??
          ????????????GatewayEntity?value?=?entry.getValue();??
          ????????????//??沒有上一個(gè)handler的就是第一個(gè)??
          ????????????if?(value.getPreHandlerId()?==?null)?{??
          ????????????????return?value;??
          ????????????}??
          ????????}??
          ????????return?null;??
          ????}??
          }??
          ??
          public?class?GatewayHandlerEnumFactory?{??
          ??
          ????private?static?GatewayDao?gatewayDao?=?new?GatewayImpl();??
          ??
          ????//?提供靜態(tài)方法,獲取第一個(gè)handler??
          ????public?static?GatewayHandler?getFirstGatewayHandler()?{??
          ??
          ????????GatewayEntity?firstGatewayEntity?=?gatewayDao.getFirstGatewayEntity();??
          ????????GatewayHandler?firstGatewayHandler?=?newGatewayHandler(firstGatewayEntity);??
          ????????if?(firstGatewayHandler?==?null)?{??
          ????????????return?null;??
          ????????}??
          ??
          ????????GatewayEntity?tempGatewayEntity?=?firstGatewayEntity;??
          ????????Integer?nextHandlerId?=?null;??
          ????????GatewayHandler?tempGatewayHandler?=?firstGatewayHandler;??
          ????????//?迭代遍歷所有handler,以及將它們鏈接起來??
          ????????while?((nextHandlerId?=?tempGatewayEntity.getNextHandlerId())?!=?null)?{??
          ????????????GatewayEntity?gatewayEntity?=?gatewayDao.getGatewayEntity(nextHandlerId);??
          ????????????GatewayHandler?gatewayHandler?=?newGatewayHandler(gatewayEntity);??
          ????????????tempGatewayHandler.setNext(gatewayHandler);??
          ????????????tempGatewayHandler?=?gatewayHandler;??
          ????????????tempGatewayEntity?=?gatewayEntity;??
          ????????}??
          ????//?返回第一個(gè)handler??
          ????????return?firstGatewayHandler;??
          ????}??
          ??
          ????/**??
          ?????*?反射實(shí)體化具體的處理者??
          ?????*?@param?firstGatewayEntity??
          ?????*?@return??
          ?????*/
          ??
          ????private?static?GatewayHandler?newGatewayHandler(GatewayEntity?firstGatewayEntity)?{??
          ????????//?獲取全限定類名??
          ????????String?className?=?firstGatewayEntity.getConference();???
          ????????try?{??
          ????????????//?根據(jù)全限定類名,加載并初始化該類,即會(huì)初始化該類的靜態(tài)段??
          ????????????Class<?>?clazz?=?Class.forName(className);??
          ????????????return?(GatewayHandler)?clazz.newInstance();??
          ????????}?catch?(ClassNotFoundException?|?IllegalAccessException?|?InstantiationException?e)?{??
          ????????????e.printStackTrace();??
          ????????}??
          ????????return?null;??
          ????}??
          ??
          ??
          }??
          ??
          public?class?GetewayClient?{??
          ????public?static?void?main(String[]?args)?{??
          ????????GetewayHandler?firstGetewayHandler?=?GetewayHandlerEnumFactory.getFirstGetewayHandler();??
          ????????firstGetewayHandler.service();??
          ????}??
          }??

          結(jié)語

          設(shè)計(jì)模式有很多,責(zé)任鏈只是其中的一種,我覺得很有意思,非常值得一學(xué)。設(shè)計(jì)模式確實(shí)是一門藝術(shù),仍需努力呀!

          來源:blog.csdn.net/q1472750149/article/

          details/121886327

          推薦

          讀者問:省廳選調(diào) 和 阿里開發(fā)崗怎么選?

          3 年開發(fā),不會(huì)循環(huán)刪除 List 中的元素,有這么難么?

          PS:因?yàn)楣娞柶脚_(tái)更改了推送規(guī)則,如果不想錯(cuò)過內(nèi)容,記得讀完點(diǎn)一下 “在看” ,加個(gè) “星標(biāo)” ,這樣每次新文章推送才會(huì)第一時(shí)間出現(xiàn)在你的訂閱列表里。 點(diǎn)“在看”支持我們吧!

          瀏覽 83
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <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>
                  18禁网站91 | 精品久久久久久久久久久久久久久 | 伊人大欧美 | 国产又黄又粗又硬 | 强上app在线观看一区二区三区 |