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

          一文弄懂責任鏈設計模式

          共 22590字,需瀏覽 46分鐘

           ·

          2022-06-26 19:36

          目錄
          • 背景

          • 什么是責任鏈

          • 使用場景

          • 結(jié)語


          背景


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


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


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


          什么是責任鏈


          責任鏈模式是一種行為設計模式, 允許你將請求沿著處理者鏈進行發(fā)送。收到請求后, 每個處理者均可對請求進行處理, 或?qū)⑵鋫鬟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關通過){
                                  //...
                              }
                          }
                      } 
                  }
              }
          }


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


          | 初步改造

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


          這樣客戶端就不需要進行多重 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);//第二關的下一關是第三關

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

              }
          }


          | 缺點

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

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


          | 責任鏈改造

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

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

          • 抽象處理者(Handler)角色:定義一個處理請求的接口,包含抽象處理方法和一個后繼連接。
          • 具體處理者(Concrete Handler)角色:實現(xiàn)抽象處理者的處理方法,判斷能否處理本次請求,如果可以處理請求則處理,否則將該請求轉(zhuǎ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)的配置請求鏈并且將每個請求者形成一條調(diào)用鏈。


          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<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();
                      //  沒有上一個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();
              }
          }


          結(jié)語


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

          推薦閱讀:

          世界的真實格局分析,地球人類社會底層運行原理

          不是你需要中臺,而是一名合格的架構師(附各大廠中臺建設PPT)

          企業(yè)IT技術架構規(guī)劃方案

          論數(shù)字化轉(zhuǎn)型——轉(zhuǎn)什么,如何轉(zhuǎn)?

          華為干部與人才發(fā)展手冊(附PPT)

          企業(yè)10大管理流程圖,數(shù)字化轉(zhuǎn)型從業(yè)者必備!

          【中臺實踐】華為大數(shù)據(jù)中臺架構分享.pdf

          華為的數(shù)字化轉(zhuǎn)型方法論

          華為如何實施數(shù)字化轉(zhuǎn)型(附PPT)

          超詳細280頁Docker實戰(zhàn)文檔!開放下載

          華為大數(shù)據(jù)解決方案(PPT)

          ????????????
          瀏覽 27
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

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

          手機掃一掃分享

          分享
          舉報
          <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>
                  avtt资源搜索 | 俺去啦电影网 | 中国东北农村乱子毛片 | 欧美成人性爱在线观看 | 在哪能看爱爱网站 |