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>

        多重校驗(yàn)神器責(zé)任鏈模式!

        共 11882字,需瀏覽 24分鐘

         ·

        2022-09-17 16:23

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

        你來(lái),我們一起精進(jìn)!你不來(lái),我和你的競(jìng)爭(zhēng)對(duì)手一起精進(jìn)!

        編輯:業(yè)余草

        juejin.cn/post/7011490664714240008

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

        背景

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

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

        這時(shí)候我就想到了用「責(zé)任鏈模式」來(lái)進(jìn)行優(yōu)化解決。

        定義

        責(zé)任鏈模式(Chain of Responsibility Pattern)是將鏈中每一個(gè)節(jié)點(diǎn)看作是一個(gè)對(duì)象,每個(gè)節(jié)點(diǎn)處理的請(qǐng)求均不同,且內(nèi)部自動(dòng)維護(hù)一個(gè)下一節(jié)點(diǎn)對(duì)象。當(dāng)一個(gè)請(qǐng)求從鏈?zhǔn)降氖锥税l(fā)出時(shí),會(huì)沿著鏈的路徑依次傳遞給每一個(gè)節(jié)點(diǎn)對(duì)象,直至有對(duì)象處理這個(gè)請(qǐng)求為止。屬于行為型模式。

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

        通用UML類圖

        責(zé)任鏈模式

        例子

        下面寫一個(gè)登錄驗(yàn)證判斷的例子,一般責(zé)任鏈模式會(huì)搭配著「建造者模式」一起用,即「鏈?zhǔn)骄幊獭?/strong>。因?yàn)檫@樣鏈條看起來(lái)更加清晰明了,而傳統(tǒng)的寫法很抽象,很難看出誰(shuí)誰(shuí)誰(shuí)在誰(shuí)的前面,誰(shuí)誰(shuí)誰(shuí)在誰(shuí)的后面,如下所示:

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

        下面先創(chuàng)建一個(gè) Handler 的抽象類,這個(gè)類里面有一個(gè)下一個(gè) Handler 處理器 next,還有一個(gè) Builder,這個(gè)就是用來(lái)構(gòu)建鏈的,也是方便我們的鏈?zhǔn)骄幊獭?/p>

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

        下面寫非空校驗(yàn) ValidateHandler 類,這里面先判斷用戶名和密碼是否為空,空的話返回,非空的話判斷 next 是否為空,非空的話就丟給下一個(gè)處理器去執(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)建登錄檢驗(yàn)LoginHandler類,判斷賬號(hào)密碼是否正確

        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)限檢驗(yàn) AuthHandler 類,判斷角色是否有權(quán)限

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

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

        public class BusinessLogicHandler extends Handler {

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

        好,下面寫個(gè)測(cè)試類來(lái)測(cè)試一下

        public class Test {

            public static void main(String[] args) {
                Handler.Builder builder = new Handler.Builder();
                //這里就是鏈?zhǔn)骄幊?,誰(shuí)在前誰(shuí)在后看的清清楚楚,明明白白
                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)限都設(shè)置正確

        此時(shí)所有驗(yàn)證都通過(guò),開(kāi)始執(zhí)行業(yè)務(wù)邏輯了

        源碼中的應(yīng)用

        我們來(lái)看一個(gè)J2EE標(biāo)準(zhǔn)中非常常見(jiàn)的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() {}
        }

        這個(gè)Filter接口的定義非常簡(jiǎn)單,相當(dāng)于責(zé)任鏈模型中的handler抽象角色,我們來(lái)看Spring中的實(shí)現(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(),這個(gè)就相當(dāng)于 for 循環(huán)的執(zhí)行 filters 中的 Filter 方法。「雖然寫法不同,但也起到了責(zé)任鏈的功能,所以在學(xué)習(xí)設(shè)計(jì)模式中,不要拘泥于標(biāo)準(zhǔn)的寫法,很多都是變種的,或者寫著寫著四不像的模式,既像這個(gè)設(shè)計(jì)模式,又像那個(gè)設(shè)計(jì)模式,這個(gè)很正常,能起到精簡(jiǎn)代碼,高效運(yùn)行的都是好代碼?!?/strong>

        優(yōu)缺點(diǎn)

        優(yōu)點(diǎn):

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

        缺點(diǎn):

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

        瀏覽 42
        點(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>

          <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            亚洲操屄视频 | 被夫上司蹂躏的七天七夜在线观看 | 国产精品 视频瘾无码 | 国产无码免费视频 | 国产精品久久久久久久三级 | 日本黄色电影免费网站 | 人人操人人摸人人看 | 做爱网站视频免费播放 | 黄色电影亚洲 | 宅男噜噜噜区一区二区三 |