1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

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

        共 12040字,需瀏覽 25分鐘

         ·

        2023-05-15 14:21

        目錄

        • 背景
        • 什么是責(zé)任鏈
        • 使用場(chǎng)景
        • 結(jié)語(yǔ)

        背景

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

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

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

        什么是責(zé)任鏈

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

        a0e8fa80309d69881f606fb5e02c4676.webp圖片

        使用場(chǎng)景

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

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

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

        • Java 過(guò)濾器的底層實(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)通過(guò)){??
        ????//?第2關(guān)?游戲??
        ????if(第2關(guān)通過(guò)){??
        ????????//?第3關(guān)?游戲??
        ????????if(第3關(guān)通過(guò)){??
        ???????????//?第4關(guān)?游戲??
        ????????????if(第4關(guān)通過(guò)){??
        ????????????????//?第5關(guān)?游戲??
        ????????????????if(第5關(guān)通過(guò)){??
        ????????????????????//?第6關(guān)?游戲??
        ????????????????????if(第6關(guān)通過(guò)){??
        ????????????????????????//...??
        ????????????????????}??
        ????????????????}??
        ????????????}???
        ????????}??
        ????}??
        }??

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

        | 初步改造

        如何解決這個(gè)問(wèn)題,我們可以通過(guò)鏈表將每一關(guān)連接起來(lái),形成責(zé)任鏈的方式,第一關(guān)通過(guò)后是第二關(guān),第二關(guān)通過(guò)后是第三關(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),因此沒(méi)有下一關(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)??
        ??
        ????????//說(shuō)明:因?yàn)榈谌P(guān)是最后一關(guān),因此沒(méi)有下一關(guān)??
        ????????//開(kāi)始調(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)。

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

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

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

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

        16918d5d0ab2c1204f7d90732ad40c79.webp圖片
            public?abstract?class?AbstractHandler?{??
        ??
        ????/**??
        ?????*?下一關(guān)用當(dāng)前抽象類來(lái)接收??
        ?????*/
        ??
        ????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)??
        ??
        ????????//?和上面沒(méi)有更改的客戶端代碼相比,只有這里的set方法發(fā)生變化,其他都是一樣的??
        ????????firstPassHandler.setNext(secondPassHandler);//第一關(guān)的下一關(guān)是第二關(guān)??
        ????????secondPassHandler.setNext(thirdPassHandler);//第二關(guān)的下一關(guān)是第三關(guān)??
        ??
        ????????//說(shuō)明:因?yàn)榈谌P(guān)是最后一關(guān),因此沒(méi)有下一關(guān)??
        ??
        ????????//從第一個(gè)關(guān)卡開(kāi)始??
        ????????firstPassHandler.handler();??
        ??
        ????}??
        }??

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

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

        微信搜索公眾號(hà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();??
        ????????????//??沒(méi)有上一個(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,以及將它們鏈接起來(lái)??
        ????????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é)語(yǔ)

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

        來(lái)源:blog.csdn.net/q1472750149/article/

        details/121886327

        推薦

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

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

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

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

        手機(jī)掃一掃分享

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

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            涩涩999 | 久久精品视频在线观看 | 色综合久久五月丁香花 | 男生下面伸进女人下面的视频 | 成品短视频app软件大全苹果版 | 澳门成人网站 | 帅哥操美女免费网站 | 少妇双乳好大有奶水视频 | 韩国午夜福利 | 含着她的花蒂咬到高潮免费视频 |