首页 > 其他分享 >一文带你读懂设计模式之责任链模式

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

时间:2023-08-18 17:36:14浏览次数:38  
标签:handle 一文 void request response 读懂 设计模式 public 请求

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.总结

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

作者:京东科技 宋慧超

来源:京东云开发者社区

标签:handle,一文,void,request,response,读懂,设计模式,public,请求
From: https://www.cnblogs.com/Jcloud/p/17641111.html

相关文章

  • 一文读懂LockSupport
    阅读本文前,需要储备的知识点如下,点击链接直接跳转。java线程详解Java不能操作内存?Unsafe了解一下LockSupport介绍搞java开发的基本都知道J.U.C并发包(即java.util.concurrent包),所有并发相关的类基本都来自于这个包下,这个包是JDK1.5以后由祖师爷DougLea写的,LockSupport也是在这......
  • 设计模式-代理模式
    代理模式(文章目录)什么是代理模式  代理模式(ProxyDesignPattern)的原理和代码实现都不难掌握。它在不改变原始类(或叫被代理类)代码的情况下,通过引入代理类来给原始类附加功能。为什么要用代理模式职责清晰,功能增强部分代码可以放到代理类中,减少与业务代码的耦合;更符合......
  • 一文玩转Apipost
    前言Apipost是一款支持RESTfulAPI、SOAPAPI、GraphQLAPI等多种API类型,支持HTTPS、WebSocket、gRPC多种通信协议的API调试工具。除此之外,Apipost还提供了自动化测试、团队协作、等多种功能。这些丰富的功能简化了工作流程,提高了研发效率,这也让Apipost成为开发人员首选的API......
  • 一文玩转Apipost
    前言 Apipost是一款支持RESTfulAPI、SOAPAPI、GraphQLAPI等多种API类型,支持HTTPS、WebSocket、gRPC多种通信协议的API调试工具。除此之外,Apipost还提供了自动化测试、团队协作、等多种功能。这些丰富的功能简化了工作流程,提高了研发效率,这也让Apipost成为开发人员首选的A......
  • 设计模式(二)工厂方法
    一、前言工厂模式常见的词:简单工厂、工厂方法、抽象工厂。简单工厂不属于23种经典设计模式,但通常将它作为学习其他工厂模式的基础。二、简单工厂1、定义定义一个工厂类,它可以根据参数的不同返回不同类型的实例,被创建的实例通常都具有共同的父类。由于简单的工厂模式中用于创建......
  • 一文带你读懂设计模式之责任链模式 | 京东云技术团队
    1.前言emm,翻了一下之前刚入职时候的学习笔记,发现之前在熟悉业务代码的时候曾经专门学习并整理过过设计模式中的责任链模式,之前只是对其简单了解过常用的设计模式有哪些,并未结合实例和源码深入对其探究,利用熟悉代码契机进行系统学习并整理文档如下。2.什么是责任链模式?俗话说没有规......
  • Prototype 原型模式简介与 C# 示例【创建型4】【设计模式来了_4】
    〇、简介1、什么是原型模式?一句话解释:  针对比较耗时的对象创建过程,通过原型的Clone方法来克隆对象,而非重新创建。原型设计模式(PrototypeDesignPattern)是一种创建型设计模式,其主要目的是通过复制已有对象来创建新的对象,而无需通过实例化类并初始化其属性。这种模式在需......
  • 16 策略模式 -- go语言设计模式
    策略模式是一种行为设计模式,通过策略模式,可以在运行时修改一个对象的行为。很多资料里对它的定义是:定义一类算法族,将每个算法分别封装起来,让他们可以互相替换,此模式让算法的变化独立于使用算法的客户端。策略模式的实现代码packagemainimport"fmt"/* 练习: 商场促销有策......
  • JS常见的设计模式
    单例模式点击查看代码<script>//单例模式=>自始至终都只能创建一个对象让对象唯一存在letWife=(function(){classPerson{constructor(name){this.name=name}}......
  • 一文揭露AI聊天机器人到底是怎么实现自助应答的
    现在很多的企业都会使用客服系统,主要是想通过它们来解决企业的一些问题和需求。所有就衍生了——AI聊天机器人这个新工具,它是把AI人工智能运用到客户服务当中,让AI来帮助我们完成一些解答客户问题的操作。下面我们就来说一下AI聊天机器人是如何实现自动应答的吧!AI聊天机器人怎么实现......