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

          多重校驗神器責任鏈模式!

          共 11882字,需瀏覽 24分鐘

           ·

          2022-09-17 16:23

          你知道的越多,不知道的就越多,業(yè)余的像一棵小草!

          你來,我們一起精進!你不來,我和你的競爭對手一起精進!

          編輯:業(yè)余草

          juejin.cn/post/7011490664714240008

          推薦:https://www.xttblog.com/?p=5357

          背景

          最近在做需求,寫一個方法,先在前面做驗證,if 不滿足 A 條件則 return,if 不滿足 B 條件則 return...一共寫了 5 個驗證,等驗證通過以后才執(zhí)行下面的邏輯,這個也沒問題。過了一陣產(chǎn)品提了需求,跟這個方法類似,我又把這個方法 copy 了一份,只不過驗證條件稍微有點不一樣,要變成 6 個驗證了。

          這時候我就發(fā)現(xiàn)有三個問題,第一重復代碼,相同的 A 條件 B 條件 C 條件寫了兩份,沒有復用。第二,“頭重腳輕”,比如 100 行的方法,前面 60 行都是驗證,后面 40 行才是真正有用的業(yè)務代碼,你看一個方法功能的時候前面驗證肯定是不關(guān)心的,只看后面 40 行到底在干什么邏輯,所以要縮短驗證代碼的行數(shù)。第三,先后順序,一個方法A是在B之前驗證的,另一個方法 A 是在 B 之后驗證的,調(diào)整起來很不方便。

          這時候我就想到了用「責任鏈模式」來進行優(yōu)化解決。

          定義

          責任鏈模式(Chain of Responsibility Pattern)是將鏈中每一個節(jié)點看作是一個對象,每個節(jié)點處理的請求均不同,且內(nèi)部自動維護一個下一節(jié)點對象。當一個請求從鏈式的首端發(fā)出時,會沿著鏈的路徑依次傳遞給每一個節(jié)點對象,直至有對象處理這個請求為止。屬于行為型模式。

          生活中的應用場景就是「審批流」。責任鏈模式主要是解耦了請求與處理,客戶只需將請求發(fā)送到鏈上即可,無需關(guān)心請求的具體內(nèi)容和處理細節(jié),請求會自動進行傳遞直至有節(jié)點對象進行處理。

          通用UML類圖

          責任鏈模式

          例子

          下面寫一個登錄驗證判斷的例子,一般責任鏈模式會搭配著「建造者模式」一起用,即「鏈式編程」。因為這樣鏈條看起來更加清晰明了,而傳統(tǒng)的寫法很抽象,很難看出誰誰誰在誰的前面,誰誰誰在誰的后面,如下所示:

          AAAHandler.setNextHandler(deptManagerLeaveHandler);
          directLeaderLeaveHandler.setNextHandler(deptManagerLeaveHandler);
          BBBHandler.setNextHandler(AAAHandler);
          deptManagerLeaveHandler.setNextHandler(gManagerLeaveHandler);

          下面先創(chuàng)建一個 Handler 的抽象類,這個類里面有一個下一個 Handler 處理器 next,還有一個 Builder,這個就是用來構(gòu)建鏈的,也是方便我們的鏈式編程。

          public abstract class Handler<T{

              protected Handler next;

              private void next(Handler next) {
                  this.next = next;
              }

              public abstract void doHandler(Member member);

              public static class Builder<T{
                  private Handler<T> head;
                  private Handler<T> tail;

                  public Builder<T> addHandler(Handler handler) {
                      if (this.head == null) {
                          this.head = this.tail = handler;
                          return this;
                      }
                      this.tail.next(handler);
                      this.tail = handler;
                      return this;
                  }

                  public Handler<T> build() {
                      return this.head;
                  }
              }
          }

          下面寫非空校驗 ValidateHandler 類,這里面先判斷用戶名和密碼是否為空,空的話返回,非空的話判斷 next 是否為空,非空的話就丟給下一個處理器去執(zhí)行。

          public class ValidateHandler extends Handler {
              @Override
              public void doHandler(Member member) {
                  if (StringUtils.isEmpty(member.getUsername()) ||
                          StringUtils.isEmpty(member.getPassword())) {
                      System.out.println("用戶名和密碼不能為空");
                      return;
                  }
                  if (null != next) {
                      next.doHandler(member);
                  }
              }
          }

          創(chuàng)建登錄檢驗LoginHandler類,判斷賬號密碼是否正確

          public class LoginHandler extends Handler {

              @Override
              public void doHandler(Member member) {
                  if (!"jack".equals(member.getUsername()) || !"666".equals(member.getPassword())) {
                      System.out.println("用戶名密碼不正確");
                      return;
                  }
                  if (null != next) {
                      next.doHandler(member);
                  }
              }
          }

          創(chuàng)建權(quán)限檢驗 AuthHandler 類,判斷角色是否有權(quán)限

          public class AuthHandler extends Handler {
              @Override
              public void doHandler(Member member) {
                  if (!"管理員".equals(member.getRoleName())) {
                      System.out.println("您不是管理員,沒有操作權(quán)限");
                      return;
                  }
                  if (null != next) {
                      next.doHandler(member);
                  }
              }
          }

          創(chuàng)建執(zhí)行業(yè)務邏輯類

          public class BusinessLogicHandler extends Handler {

              @Override
              public void doHandler(Member member) {
                  System.out.println("執(zhí)行業(yè)務邏輯。。");
              }
          }

          好,下面寫個測試類來測試一下

          public class Test {

              public static void main(String[] args) {
                  Handler.Builder builder = new Handler.Builder();
                  //這里就是鏈式編程,誰在前誰在后看的清清楚楚,明明白白
                  builder.addHandler(new ValidateHandler())
                          .addHandler(new LoginHandler())
                          .addHandler(new AuthHandler())
                          .addHandler(new BusinessLogicHandler());
                  Member member = new Member();
                  member.setUsername("");
                  member.setPassword("");
                  builder.build().doHandler(member);
              }

          }

          執(zhí)行一下,提示用戶名密碼不能為空

          修改下用戶名和密碼

          執(zhí)行一下,提示用戶名密碼不正確

          直到把用戶名密碼權(quán)限都設置正確

          此時所有驗證都通過,開始執(zhí)行業(yè)務邏輯了

          源碼中的應用

          我們來看一個J2EE標準中非常常見的Filter類:

          public interface Filter {
              public default void init(FilterConfig filterConfig) throws ServletException {}

              public void doFilter(ServletRequest request, ServletResponse response,
                      FilterChain chain)
           throws IOException, ServletException
          ;

              public default void destroy() {}
          }

          這個Filter接口的定義非常簡單,相當于責任鏈模型中的handler抽象角色,我們來看Spring中的實現(xiàn)MockFilterChain類:

          public class MockFilterChain implements FilterChain {

             @Nullable
             private ServletRequest request;
             @Nullable
             private ServletResponse response;
             private final List<Filter> filters;
             @Nullable
             private Iterator<Filter> iterator;

             public MockFilterChain() {
                this.filters = Collections.emptyList();
             }

             public MockFilterChain(Servlet servlet) {
                this.filters = initFilterList(servlet);
             }

             public MockFilterChain(Servlet servlet, Filter... filters) {
                Assert.notNull(filters, "filters cannot be null");
                Assert.noNullElements(filters, "filters cannot contain null values");
                this.filters = initFilterList(servlet, filters);
             }

             private static List<Filter> initFilterList(Servlet servlet, Filter... filters) {
                Filter[] allFilters = ObjectUtils.addObjectToArray(filters, new ServletFilterProxy(servlet));
                return Arrays.asList(allFilters);
             }

             @Nullable
             public ServletRequest getRequest() {
                return this.request;
             }

             @Nullable
             public ServletResponse getResponse() {
                return this.response;
             }

             @Override
             public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException {
                Assert.notNull(request, "Request must not be null");
                Assert.notNull(response, "Response must not be null");
                Assert.state(this.request == null"This FilterChain has already been called!");

                if (this.iterator == null) {
                   this.iterator = this.filters.iterator();
                }
                //核心代碼執(zhí)行
                if (this.iterator.hasNext()) {
                   Filter nextFilter = this.iterator.next();
                   nextFilter.doFilter(request, response, this);
                }

                this.request = request;
                this.response = response;
             }

             public void reset() {
                this.request = null;
                this.response = null;
                this.iterator = null;
             }

                 ...
          }

          這里面把鏈條中所有的 Filter 都放到List<Filter> filters中, 在 doFilter() 方法中有一段核心的代碼this.iterator.hasNext(),這個就相當于 for 循環(huán)的執(zhí)行 filters 中的 Filter 方法。「雖然寫法不同,但也起到了責任鏈的功能,所以在學習設計模式中,不要拘泥于標準的寫法,很多都是變種的,或者寫著寫著四不像的模式,既像這個設計模式,又像那個設計模式,這個很正常,能起到精簡代碼,高效運行的都是好代碼。」

          優(yōu)缺點

          優(yōu)點:

          1. 將請求與處理解耦。
          2. 請求處理者(節(jié)點對象)只需關(guān)注自己感興趣的請求進行處理,對于不感興趣的請求,直接轉(zhuǎn)發(fā)給下一級節(jié)點對象。
          3. 具備鏈式傳遞處理請求功能,請求發(fā)送者無需知曉鏈路結(jié)構(gòu),只需等待請求處理結(jié)果。
          4. 鏈路結(jié)構(gòu)靈活,可以通過改變鏈路結(jié)構(gòu)動態(tài)地新增或刪減責任。
          5. 易于擴展新的請求處理類(節(jié)點),符合開閉原則。

          缺點:

          1. 責任鏈太長或者處理時間過長,會影響整體性能。
          2. 如果節(jié)點對象存在循環(huán)引用時,會造成死循環(huán),導致系統(tǒng)崩潰。

          瀏覽 42
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

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

          手機掃一掃分享

          分享
          舉報
          <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>
                  娱乐网一区二区三区 | 三级黄色电影院 | 亚洲欧美中文字幕在线观看 | 日韩中文字幕在线免费视频 | 欧美成人黄色免费网站 |