一文带你读懂设计模式之责任链模式

一文,读懂,设计模式,责任,模式 · 浏览次数 : 71

小编点评

**责任链模式** 责任链模式是一种常见的设计模式,各个不同职责的处理器串联起来,通过一次请求便能够执行完每个处理器的处理方法。 **链路结构** 责任链模式的链路结构通常使用数组或列表来存储各个处理器的地址。每个处理器在它处理的请求中存储其地址,并通过这些地址向下一个处理器发送请求。最终,请求的接送方只关心自己的处理逻辑,自己处理完成之后将请求传递给下一个接收者。从而实现请求发送者和接收者的解耦。 **数组和列表** 责任链模式中可以使用数组或列表来存储各个处理器的地址。数组使用索引访问地址,列表使用元素索引访问地址。 **示例** ```java //责任链模式的链路结构 List interceptors = new ArrayList<>(); interceptors.add(new Interceptor1()); interceptors.add(new Interceptor2()); interceptors.add(new Interceptor3()); //执行责任链模式 for (HandlerInterceptor interceptor : interceptors) { interceptor.preHandle(request, response, handler); } handler.postHandle(request, response, mv); handler.afterCompletion(request, response, mv); ``` **结论** 责任链模式是一种常见的设计模式,可以将多个处理器的逻辑组合起来,通过一次请求便能够执行完每个处理器的处理方法。责任链模式的链路结构通常使用数组或列表来存储各个处理器的地址。

正文

1.前言

emm,翻了一下之前刚入职时候的学习笔记,发现之前在熟悉业务代码的时候曾经专门学习并整理过过设计模式中的责任链模式,之前只是对其简单了解过常用的设计模式有哪些,并未结合实例和源码深入对其探究,利用熟悉代码契机进行系统学习并整理文档如下。

2.什么是责任链模式?

俗话说没有规矩不成方圆,我们无论在工作还是生活中很多事情都需要按照规定的流程办事,这样的流程往往都是环环相扣的,上一部完成之后才会流转到下一步执行。比如我们在做饭时都是先买菜、洗菜、切菜、炒菜、装盘在这样的过程中只有上一步完成之后才能开始下一步最后得到一道做好的菜;又比如在晋升提名时,首先我们要做一个述职报告进行述职,然后就是评审小组打分,评审小组筛选通过后,流转到项目组领导处审批,项目组领导根据述职报告和评审小组分数决定是否晋升,项目组领导同意之后最后流转到部门领导审批并给出最后结果。像这种一步一步完成流程都可以通过责任链模式来实现。

  • 简介: 责任链模式顾名思义是将不同职责的步骤串联起来执行,并且一个步骤执行完成之后才能够执行下一个步骤。从名字可以看出通常责任链模式使用链表来完成。因此当执行任务的请求发起时,从责任链上第一步开始往下传递,直到最后一个步骤完成。在责任链模式当中,客户端只用执行一次流程开始的请求便不再需要参与到流程执行当中,责任链上的流程便能够自己一直往下执行,客户端同样也并不关心执行流程细节,从而实现与流程之间的解耦。

  • 模式结构:责任链模式主要角色如下:

抽象处理器(Handler):处理器抽象接口,定义了处理请求的方法和执行下一步处理的处理器。

具体处理器(ConcreteHandler):执行请求的具体实现,先根据请求执行处理逻辑,完成之后将请求交给下一个处理器执行。

调用者:调用者通过创建处理器并将请求交给处理器进行处理。

  • 相关代码:
// 抽象处理器
public abstract class Handler {
    private Handler next;

    public Handler getNext() {
        return next;
    }

    public void setNext(Handler next) {
        this.next = next;
    }
    public abstract void handle(Object request);
}

// 具体处理器 1
public class ConcreteHandler1 extends Handler {
    @Override
    public void handle(Object request) {
        System.out.println("concrete handler 1 execute request. request: " + request);
        if (getNext() != null) {
            getNext().handle(request);
        }
    }
}

// 具体处理器 2
public class ConcreteHandler2 extends Handler {
    @Override
    public void handle(Object request) {
        System.out.println("concrete handler 2 execute request. request: " + request);
        if (getNext() != null){
            getNext().handle(request);
        }
    }
}

// 具体处理器 3
public class ConcreteHandler3 extends Handler {
    @Override
    public void handle(Object request) {
        System.out.println("concrete handler 3 execute request. request: " + request);
        if (getNext() != null) {
            getNext().handle(request);
        }
    }
}


public static void main(String[] args) {
    Handler concreteHandler1 = new ConcreteHandler1();
    Handler concreteHandler2 = new ConcreteHandler2();
    Handler concreteHandler3 = new ConcreteHandler3();

    concreteHandler1.setNext(concreteHandler2);
    concreteHandler2.setNext(concreteHandler3);

    concreteHandler1.handle("my request.");
}



从上面的代码我们可以看到其实责任链模式是非常简单的,但是其中有几个点需要注意一下:

  • 首先我们需要对整个责任链进行初始化,即设置每个处理器的 next
  • 在每个具体处理器处理完之后需要手动调用下一个处理器的 handle 方法来执行下一步处理,这里其实还可以使用模板方法模式进行优化。

控制台输出如下:

concrete handler 1 execute request. request: my request.
concrete handler 2 execute request. request: my request.
concrete handler 3 execute request. request: my request.



3.具体实例demo

日常请假为例。请假申请会先到你的直属 leader 处审批,审批通过后再到部门 leader 处审批,部门 leader 通过后,最后到人事处报备记录请假天数。如果在传统企业里面,我们需要手写一份请假表,然后跑到直属 leader 办公室,让直属 leader 签字,然后再到部门 leader 办公室签字,最后还要跑到人事处上交请假单,这样相当于发出了三次请求,才能走完整个请假流程。

但是在现代各种 OA 系统管理下,整个请假流程就变的简单了,我们只需要发起一次请假请求,接下来你的请假请求便会自动的在审批人中间进行流转,这个时候我们的责任链模式便派上用场。代码如下:

// 请假抽象处理器
public abstract class DayOffHandler {
    private DayOffHandler next;

    public DayOffHandler getNext() {
        return next;
    }

    public void setNext(DayOffHandler next) {
        this.next = next;
    }
    public abstract void handle(String request);

}
// 直属 leader 处理
public class GroupLeaderHandler extends DayOffHandler {
    @Override
    public void handle(String request) {
        System.out.println("直属 leader 审查: " + request);
        System.out.println("同意请求");
        if (getNext() != null) {
            getNext().handle(request);
        }
    }
}
// 部门 leader 处理
public class DepartmentLeaderHandler extends DayOffHandler{
    @Override
    public void handle(String request) {
        System.out.println("部门 leader 审查: " + request);
        System.out.println("同意请求");
        if (getNext() != null) {
            getNext().handle(request);
        }
    }
}
// 人事处处理
public class HRHandler extends DayOffHandler {
    @Override
    public void handle(String request) {
        System.out.println("人事处审查: " + request);
        System.out.println("同意请求,记录请假");
        if (getNext() != null) {
            getNext().handle(request);
        }
    }
}




上面的代码定义了请假抽象处理类和三个具体的处理人,我们需要将这三个处理人的流程初始化串联起来,并且一步步的执行下去,像下面这张图所示流程一样,于是客户端的代码如下:

public static void main(String[] args) {

    DayOffHandler groupLeaderHandler = new GroupLeaderHandler();
    DayOffHandler departmentLeaderHandler = new DepartmentLeaderHandler();
    DayOffHandler hrHandler = new HRHandler();
    groupLeaderHandler.setNext(departmentLeaderHandler);
    departmentLeaderHandler.setNext(hrHandler);

    System.out.println("收到面试通知,需要请假");
    String request = "家中有事,请假半天,望批准";
    System.out.println("发起请求:");
    groupLeaderHandler.handle(request);
}



客户端代码中可以看到,我们首先实例化了三个具体处理人,然后通过 setNext 方法将他们串联起来,而我们只需向直属 leader 发起一次请假请求即可,整个审批流程便能够自动的执行下去,不需要再挨个跑办公室申请了。执行后的结果如下:

收到面试通知,需要请假
发起请求:
直属 leader 审查: 家中有事,请假半天,望批准
同意请求
部门 leader 审查: 家中有事,请假半天,望批准
同意请求
人事处审查: 家中有事,请假半天,望批准
同意请求,记录请假



4.责任链模式在源码中的应用

说到责任链模式,那么最著名的当然是 Servlet 中的过滤器 Filter 了,在这拦截器和过滤器的体系中都使用责任链模式来依次处理每个请求,首先看看过滤器 Filter 的使用方式。Filter 接口如下:

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

    void doFilter(ServletRequest var1, ServletResponse var2, FilterChain var3) throws IOException, ServletException;

    default void destroy() {
    }
}


  • FilterChain 便是过滤器 Filter 的一条责任链,其代码如下:
public interface FilterChain {
    void doFilter(ServletRequest var1, ServletResponse var2) throws IOException, ServletException;
}


public final class ApplicationFilterChain implements FilterChain {
    
    public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException {
        if (Globals.IS_SECURITY_ENABLED) {
            final ServletRequest req = request;
            final ServletResponse res = response;

            try {
                AccessController.doPrivileged(new PrivilegedExceptionAction<Void>() {
                    public Void run() throws ServletException, IOException {
                        ApplicationFilterChain.this.internalDoFilter(req, res);
                        return null;
                    }
                });
            } catch (PrivilegedActionException var7) {
                Exception e = var7.getException();
                if (e instanceof ServletException) {
                    throw (ServletException)e;
                }

                if (e instanceof IOException) {
                    throw (IOException)e;
                }

                if (e instanceof RuntimeException) {
                    throw (RuntimeException)e;
                }

                throw new ServletException(e.getMessage(), e);
            }
        } else {
            this.internalDoFilter(request, response);
        }

    }

    private void internalDoFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException {
        if (this.pos < this.n) {
            ApplicationFilterConfig filterConfig = this.filters[this.pos++];

            try {
                Filter filter = filterConfig.getFilter();
                if (request.isAsyncSupported() && "false".equalsIgnoreCase(filterConfig.getFilterDef().getAsyncSupported())) {
                    request.setAttribute("org.apache.catalina.ASYNC_SUPPORTED", Boolean.FALSE);
                }

                if (Globals.IS_SECURITY_ENABLED) {
                    Principal principal = ((HttpServletRequest)request).getUserPrincipal();
                    Object[] args = new Object[]{request, response, this};
                    SecurityUtil.doAsPrivilege("doFilter", filter, classType, args, principal);
                } else {
                    filter.doFilter(request, response, this);
                }

            } catch (ServletException | RuntimeException | IOException var15) {
                throw var15;
            } catch (Throwable var16) {
                Throwable e = ExceptionUtils.unwrapInvocationTargetException(var16);
                ExceptionUtils.handleThrowable(e);
                throw new ServletException(sm.getString("filterChain.filter"), e);
            }
        } else {
            try {
                if (ApplicationDispatcher.WRAP_SAME_OBJECT) {
                    lastServicedRequest.set(request);
                    lastServicedResponse.set(response);
                }

                if (request.isAsyncSupported() && !this.servletSupportsAsync) {
                    request.setAttribute("org.apache.catalina.ASYNC_SUPPORTED", Boolean.FALSE);
                }

                if (request instanceof HttpServletRequest && response instanceof HttpServletResponse && Globals.IS_SECURITY_ENABLED) {
                    Principal principal = ((HttpServletRequest)request).getUserPrincipal();
                    Object[] args = new Object[]{request, response};
                    SecurityUtil.doAsPrivilege("service", this.servlet, classTypeUsedInService, args, principal);
                } else {
                    this.servlet.service(request, response);
                }
            } catch (ServletException | RuntimeException | IOException var17) {
                throw var17;
            } catch (Throwable var18) {
                Throwable e = ExceptionUtils.unwrapInvocationTargetException(var18);
                ExceptionUtils.handleThrowable(e);
                throw new ServletException(sm.getString("filterChain.servlet"), e);
            } finally {
                if (ApplicationDispatcher.WRAP_SAME_OBJECT) {
                    lastServicedRequest.set((Object)null);
                    lastServicedResponse.set((Object)null);
                }

            }

        }
    }
}



  • internalDoFilter() 方法中,可以看到整个 FilterChain 上使用数组 filters 存放每一个过滤器及其配置并使用 pos 记录当前遍历到哪一个过滤器,然后再执行获取到的 FilterdoFilter 方法。与前面所讲链表方式存放不同,这里的链路使用数组来进行存放。

  • spring中的责任链模式

SpringMVC 中的 Interceptor 同样也用到了责任链模式。首先来看看 Interceptor 的抽象处理类;

public interface HandlerInterceptor {
    default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        return true;
    }

    default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {
    }

    default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {
    }
}




在抽象处理类中,定义了三个方法,分别是处理前置处理器、后置处理器和整个流程完成之后的处理。通过 HandlerExecutionChain 将拦截器串联起来,在 HandlerExecutionChain 中,我们需要关注 applyPreHandleapplyPostHandletriggerAfterCompletion 三个方法,这三个方法分别执行了拦截器中所定义的 preHandlepostHandleafterCompletion 方法。并且从代码中也能够看处,和前面的过滤器一样,所有的拦截器都存放在 interceptors 数组中,并在三个方法中遍历 interceptors 数组依次执行相应的方法

public class HandlerExecutionChain {
    @Nullable
    private HandlerInterceptor[] interceptors;

    boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
        HandlerInterceptor[] interceptors = this.getInterceptors();
        if (!ObjectUtils.isEmpty(interceptors)) {
            for(int i = 0; i < interceptors.length; this.interceptorIndex = i++) {
                HandlerInterceptor interceptor = interceptors[i];
                if (!interceptor.preHandle(request, response, this.handler)) {
                    this.triggerAfterCompletion(request, response, (Exception)null);
                    return false;
                }
            }
        }

        return true;
    }

    void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv) throws Exception {
        HandlerInterceptor[] interceptors = this.getInterceptors();
        if (!ObjectUtils.isEmpty(interceptors)) {
            for(int i = interceptors.length - 1; i >= 0; --i) {
                HandlerInterceptor interceptor = interceptors[i];
                interceptor.postHandle(request, response, this.handler, mv);
            }
        }

    }

    void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, @Nullable Exception ex) throws Exception {
        HandlerInterceptor[] interceptors = this.getInterceptors();
        if (!ObjectUtils.isEmpty(interceptors)) {
            for(int i = this.interceptorIndex; i >= 0; --i) {
                HandlerInterceptor interceptor = interceptors[i];

                try {
                    interceptor.afterCompletion(request, response, this.handler, ex);
                } catch (Throwable var8) {
                    logger.error("HandlerInterceptor.afterCompletion threw exception", var8);
                }
            }
        }

    }
}



5.总结

责任链模式也是常见的设计模式,各个不同职责的处理器串联起来,通过一次请求便能够执行完每个处理器的处理方法。通过这样的方式请求的发送者只需发出一次请求同时也不需要知道详细的链路结构;而请求的接送方只关心自己的处理逻辑,自己处理完成之后将请求传递给下一个接收者,从而完成自己的任务,这样便实现了请求发送者和接收者的解耦。而从源码分析中可以看到,责任链模式虽然常见使用链表结构,但是使用数组和列表同样能够完成需求。

作者:京东科技 宋慧超

来源:京东云开发者社区

与一文带你读懂设计模式之责任链模式相似的内容:

一文带你读懂设计模式之责任链模式

翻了一下之前刚入职时候的学习笔记,发现之前在熟悉业务代码的时候曾经专门学习并整理过过设计模式中的责任链模式,之前只是对其简单了解过常用的设计模式有哪些,并未结合实例和源码深入对其探究,利用熟悉代码契机进行系统学习并整理文档如下。

当装饰者模式遇上Read Through缓存,一场技术的浪漫邂逅

在《经验之谈:我为什么选择了这样一个激进的缓存大Key治理方案》一文中,我提到在系统中使用的缓存是旁路缓存模式,有读者朋友问,有没有用到过其他的缓存模式,本文将结合一个我曾经工作中的案例,使用装饰者模式实现Read Through缓存模式,助你轻松掌握设计模式和缓存。

一文详解数据仓库的物理细粒度备份恢复

华为云技术专家带你读懂数据仓库的物理细粒度备份恢复,包括它的优势、恢复的基本流程和具体使用实践。

[转帖]一文带你搞懂xxl-job(分布式任务调度平台)

https://zhuanlan.zhihu.com/p/625060354 前言 本篇文章主要记录项目中遇到的 xxl-job 的实战,希望能通过这篇文章告诉读者们什么是 xxl-job 以及怎么使用 xxl-job 并分享一个实战案例。 那么下面先说明什么是 xxl-job 以及为什么要使用它。

带你读AI论文丨针对文字识别的多模态半监督方法

摘要:本文提出了一种针对文字识别的多模态半监督方法,具体来说,作者首先使用teacher-student网络进行半监督学习,然后在视觉、语义以及视觉和语义的融合特征上,都进行了一致性约束。 本文分享自华为云社区《一种针对文字识别的多模态半监督方法》,作者: Hint 。 摘要 直到最近,公开的真实场

带你读论文丨S&P2019 HOLMES Real-time APT Detection

本文提出了一种实现了检测高级持久性威胁(Advanced Persistent Threat,APT)新的方法,即HOLMES系统。

【带你读论文】向量表征经典之DeepWalk

摘要:详细讲解DeepWalk,通过随机游走的方式对网络化数据做一个表示学习,它是图神经网络的开山之作,借鉴了Word2vec的思想。 本文分享自华为云社区《[论文阅读] (25) 向量表征经典之DeepWalk:从Word2vec到DeepWalk,再到Asm2vec和Log2vec》,作者:ea

【长文】带你搞明白Redis

Redis,英文全称是Remote Dictionary Server(远程字典服务),是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。 与MySQL数据库不同的是,Redis的数据是存在内存中的。它的读写速度非常快,每...

初识上位机(下):C#读写PLC数据块数据

作为一个工业自动化领域的程序员,不懂点PLC和上位机,貌似有点说不过去。这里我用两篇小文带你快速进入上位机开发领域。上一篇,我们搭建了一个PLC的模拟仿真环境,本篇我们使用C#开发一个简单的PLC数据读取和写入的应用程序。

一文读懂责任分配矩阵,解决你80%的项目难题

成功的项目管理取决于整个团队对角色和职责的理解,使用责任分配矩阵分配和定义角色是使项目保持在正轨并为成功做好准备的好方法。