<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倍,責任鏈模式y(tǒng)yds

          共 25493字,需瀏覽 51分鐘

           ·

          2023-08-30 11:44


          目錄
          • 什么是責任鏈
          • 使用場景
          • 結語



          前言


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

          實際上,針對導入功能,我認為模版方法更合適!為此,隔壁團隊也拿出我們的案例,進行了集體 code review。

          學好設計模式,且不要為了練習,強行使用!讓原本 100 行就能實現(xiàn)的功能,寫了 3000 行!對錯暫且不論,我們先一起看看責任鏈設計模式吧!


          什么是責任鏈





          責任鏈模式是一種行為設計模式, 允許你將請求沿著處理者鏈進行發(fā)送。收到請求后, 每個處理者均可對請求進行處理, 或將其傳遞給鏈上的下個處理者。



          使用場景





          責任鏈的使用場景還是比較多的:

          • 多條件流程判斷:權限控制
          • ERP 系統(tǒng)流程審批:總經(jīng)理、人事經(jīng)理、項目經(jīng)理
          • Java 過濾器的底層實現(xiàn) Filter

          如果不使用該設計模式,那么當需求有所改變時,就會使得代碼臃腫或者難以維護,例如下面的例子。


          反例

          假設現(xiàn)在有一個闖關游戲,進入下一關的條件是上一關的分數(shù)要高于 xx:

          • 游戲一共 3 個關卡
          • 進入第二關需要第一關的游戲得分大于等于 80
          • 進入第三關需要第二關的游戲得分大于等于 90

          那么代碼可以這樣寫:

          //第一關
          public class FirstPassHandler {
              public int handler(){
                  System.out.println("第一關-->FirstPassHandler");
                  return 80;
              }
          }

          //第二關
          public class SecondPassHandler {
              public int handler(){
                  System.out.println("第二關-->SecondPassHandler");
                  return 90;
              }
          }


          //第三關
          public class ThirdPassHandler {
              public int handler(){
                  System.out.println("第三關-->ThirdPassHandler,這是最后一關啦");
                  return 95;
              }
          }


          //客戶端
          public class HandlerClient {
              public static void main(String[] args) {

                  FirstPassHandler firstPassHandler = new FirstPassHandler();//第一關
                  SecondPassHandler secondPassHandler = new SecondPassHandler();//第二關
                  ThirdPassHandler thirdPassHandler = new ThirdPassHandler();//第三關

                  int firstScore = firstPassHandler.handler();
                  //第一關的分數(shù)大于等于80則進入第二關
                  if(firstScore >= 80){
                      int secondScore = secondPassHandler.handler();
                      //第二關的分數(shù)大于等于90則進入第二關
                      if(secondScore >= 90){
                          thirdPassHandler.handler();
                      }
                  }
              }
          }

          那么如果這個游戲有 100 關,我們的代碼很可能就會寫成這個樣子:

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

          這種代碼不僅冗余,并且當我們要將某兩關進行調整時會對代碼非常大的改動,這種操作的風險是很高的,因此,該寫法非常糟糕。

          初步改造

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

          這樣客戶端就不需要進行多重 if 的判斷了:

          public class FirstPassHandler {
              /**
               * 第一關的下一關是 第二關
               */

              private SecondPassHandler secondPassHandler;

              public void setSecondPassHandler(SecondPassHandler secondPassHandler) {
                  this.secondPassHandler = secondPassHandler;
              }

              //本關卡游戲得分
              private int play(){
                  return 80;
              }

              public int handler(){
                  System.out.println("第一關-->FirstPassHandler");
                  if(play() >= 80){
                      //分數(shù)>=80 并且存在下一關才進入下一關
                      if(this.secondPassHandler != null){
                          return this.secondPassHandler.handler();
                      }
                  }

                  return 80;
              }
          }

          public class SecondPassHandler {

              /**
               * 第二關的下一關是 第三關
               */

              private ThirdPassHandler thirdPassHandler;

              public void setThirdPassHandler(ThirdPassHandler thirdPassHandler) {
                  this.thirdPassHandler = thirdPassHandler;
              }

              //本關卡游戲得分
              private int play(){
                  return 90;
              }

              public int handler(){
                  System.out.println("第二關-->SecondPassHandler");

                  if(play() >= 90){
                      //分數(shù)>=90 并且存在下一關才進入下一關
                      if(this.thirdPassHandler != null){
                          return this.thirdPassHandler.handler();
                      }
                  }

                  return 90;
              }
          }

          public class ThirdPassHandler {

              //本關卡游戲得分
              private int play(){
                  return 95;
              }

              /**
               * 這是最后一關,因此沒有下一關
               */

              public int handler(){
                  System.out.println("第三關-->ThirdPassHandler,這是最后一關啦");
                  return play();
              }
          }

          public class HandlerClient {
              public static void main(String[] args) {

                  FirstPassHandler firstPassHandler = new FirstPassHandler();//第一關
                  SecondPassHandler secondPassHandler = new SecondPassHandler();//第二關
                  ThirdPassHandler thirdPassHandler = new ThirdPassHandler();//第三關

                  firstPassHandler.setSecondPassHandler(secondPassHandler);//第一關的下一關是第二關
                  secondPassHandler.setThirdPassHandler(thirdPassHandler);//第二關的下一關是第三關

                  //說明:因為第三關是最后一關,因此沒有下一關
                  //開始調用第一關 每一個關卡是否進入下一關卡 在每個關卡中判斷
                  firstPassHandler.handler();

              }
          }

          缺點

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

          • 每個關卡中都有下一關的成員變量并且是不一樣的,形成鏈很不方便
          • 代碼的擴展性非常不好

          責任鏈改造

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

          有了思路,我們先來簡單介紹一下責任鏈設計模式的基本組成:

          • 抽象處理者(Handler)角色: 定義一個處理請求的接口,包含抽象處理方法和一個后繼連接。
          • 具體處理者(Concrete Handler)角色: 實現(xiàn)抽象處理者的處理方法,判斷能否處理本次請求,如果可以處理請求則處理,否則將該請求轉給它的后繼者。
          • 客戶類(Client)角色: 創(chuàng)建處理鏈,并向鏈頭的具體處理者對象提交請求,它不關心處理細節(jié)和請求的傳遞過程。


          public abstract class AbstractHandler {

              /**
               * 下一關用當前抽象類來接收
               */

              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("第一關-->FirstPassHandler");
                  int score = play();
                  if(score >= 80){
                      //分數(shù)>=80 并且存在下一關才進入下一關
                      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("第二關-->SecondPassHandler");

                  int score = play();
                  if(score >= 90){
                      //分數(shù)>=90 并且存在下一關才進入下一關
                      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("第三關-->ThirdPassHandler");
                  int score = play();
                  if(score >= 95){
                      //分數(shù)>=95 并且存在下一關才進入下一關
                      if(this.next != null){
                          return this.next.handler();
                      }
                  }
                  return score;
              }
          }

          public class HandlerClient {
              public static void main(String[] args) {

                  FirstPassHandler firstPassHandler = new FirstPassHandler();//第一關
                  SecondPassHandler secondPassHandler = new SecondPassHandler();//第二關
                  ThirdPassHandler thirdPassHandler = new ThirdPassHandler();//第三關

                  // 和上面沒有更改的客戶端代碼相比,只有這里的set方法發(fā)生變化,其他都是一樣的
                  firstPassHandler.setNext(secondPassHandler);//第一關的下一關是第二關
                  secondPassHandler.setNext(thirdPassHandler);//第二關的下一關是第三關

                  //說明:因為第三關是最后一關,因此沒有下一關

                  //從第一個關卡開始
                  firstPassHandler.handler();

              }
          }

          責任鏈工廠改造

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


          public enum GatewayEnum {
              // handlerId, 攔截者名稱,全限定類名,preHandlerId,nextHandlerId
              API_HANDLER(new GatewayEntity(1"api接口限流""cn.dgut.design.chain_of_responsibility.GateWay.impl.ApiLimitGatewayHandler"null2)),
              BLACKLIST_HANDLER(new GatewayEntity(2"黑名單攔截""cn.dgut.design.chain_of_responsibility.GateWay.impl.BlacklistGatewayHandler"13)),
              SESSION_HANDLER(new GatewayEntity(3"用戶會話攔截""cn.dgut.design.chain_of_responsibility.GateWay.impl.SessionGatewayHandler"2null)),
              ;

              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 獲取配置項
               * @param handlerId
               * @return
               */

              GatewayEntity getGatewayEntity(Integer handlerId);

              /**
               * 獲取第一個處理者
               * @return
               */

              GatewayEntity getFirstGatewayEntity();
          }

          public class GatewayImpl implements GatewayDao {

              /**
               * 初始化,將枚舉中配置的handler初始化到map中,方便獲取
               */

              private static Map  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  entry : gatewayEntityMap.entrySet()) {
                      GatewayEntity value = entry.getValue();
                       //  沒有上一個handler的就是第一個
                       if (value.getPreHandlerId() ==  null) {
                           return value;
                      }
                  }
                   return  null;
              }
          }

          public  class GatewayHandlerEnumFactory {

               private  static GatewayDao gatewayDao =  new GatewayImpl();

               // 提供靜態(tài)方法,獲取第一個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;
                  }
               // 返回第一個handler
                   return firstGatewayHandler;
              }

               /**
               * 反射實體化具體的處理者
               * @param firstGatewayEntity
               * @return
               */

               private static GatewayHandler newGatewayHandler(GatewayEntity firstGatewayEntity) {
                   // 獲取全限定類名
                  String className = firstGatewayEntity.getConference(); 
                   try {
                       // 根據(jù)全限定類名,加載并初始化該類,即會初始化該類的靜態(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();
              }
          }


          結語





          設計模式有很多,責任鏈只是其中的一種,我覺得很有意思,非常值得一學。設計模式確實是一門藝術,仍需努力呀!


          入骨相思知不知


          玲瓏骰子安紅豆


          入我相思門,知我相思苦,長相思兮長相憶,短相思兮無窮極。

          瀏覽 76
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

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

          手機掃一掃分享

          分享
          舉報
          <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>
                  操小逼 | 色性操逼| 不卡的自排偷排视频网站 | 三级大香蕉 | 久久久久久蜜桃 |