This article takes you to understand the chain of responsibility model of the design pattern | JD Cloud Technical Team

1 Introduction

emm, I looked through the study notes when I first joined the job, and found that I had studied and sorted out the chain of responsibility model in the design pattern when I was familiar with the business code. I just had a brief understanding of the commonly used design patterns. Without combining examples and source code to explore it in depth, take advantage of the opportunity of being familiar with the code to study systematically and organize the documents as follows.

2. What is the Chain of Responsibility pattern?

As the saying goes, there is no rule without rules. We need to follow the prescribed procedures for many things, whether in work or in life. Such procedures are often interlocking. After the previous one is completed, it will be transferred to the next step for execution. For example, when we cook, we always buy vegetables, wash vegetables, cut vegetables, fry vegetables, and put them on the plate. In this process, only after the previous step is completed can we start the next step and finally get a ready dish; another example is when nominating for promotion , first we have to make a debriefing report for debriefing, and then the review team will score. After the review team passes the screening, it will be transferred to the project team leader for approval. The project team leader will decide whether to promote according to the debriefing report and the score of the review team. After the project team leader agrees Finally, it goes to the department leader for approval and gives the final result. A step-by-step process like this can be achieved through the chain of responsibility model.

  • Introduction: The chain of responsibility model, as the name suggests, is to execute the steps of different responsibilities in series, and the next step can only be executed after the execution of one step is completed. As can be seen from the name, the Chain of Responsibility pattern is usually implemented using a linked list. Therefore, when a request to perform a task is initiated, it is passed down from the first step in the chain of responsibility until the last step is completed. In the chain of responsibility mode, the client only needs to execute the request to start the process once and no longer needs to participate in the process execution. The process on the responsibility chain can be executed continuously. The client also does not care about the details of the execution process. In order to achieve decoupling from the process.
  • Mode structure : The main roles of the chain of responsibility mode are as follows:

◦Abstract processor (Handler) : The processor abstract interface defines the method of processing requests and the processor that performs the next step.

◦Concrete handler (ConcreteHandler) : The specific implementation of the execution request, first execute the processing logic according to the request, and then pass the request to the next processor for execution.

◦Caller : The caller creates a processor and hands the request to the processor for processing .

  • Relevant code:
// 抽象处理器
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.");
}


From the above code, we can see that the Chain of Responsibility pattern is actually very simple, but there are a few points to note:

  • First of all, we need to initialize the entire responsibility chain, that is, set the next of each processor .
  • After each specific processor is processed, it is necessary to manually call the handle method of the next processor to perform the next step of processing. In fact, the template method mode can also be used here for optimization.

The console output is as follows:

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. Specific example demo

Take daily leave as an example. The leave application will be approved by your immediate leader first , and then go to the department leader for approval after the approval is passed. After the department leader approves, it will finally report to the HR department to record the number of leave days. If in a traditional enterprise, we need to write a leave form by hand, then go to the office of the direct leader, get the direct leader to sign, then go to the office of the department leader to sign, and finally go to the personnel department to submit the leave form, which is equivalent to issuing Only after three requests can the entire process of applying for leave be completed.

However, under the management of various modern OA systems, the entire leave process becomes simpler. We only need to initiate a leave request once, and then your leave request will be automatically circulated among the approvers. At this time, our responsibility chain Patterns come in handy. code show as below:

// 请假抽象处理器
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);
        }
    }
}



The above code defines the leave abstract processing class and three specific handlers. We need to connect the process initialization of these three handlers in series , and execute them step by step, as shown in the following picture, so the customer The end code is as follows:

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


As can be seen from the client code , we first instantiate three specific handlers, and then connect them in series through the setNext method, and we only need to initiate a leave request to the direct leader, and the entire approval process can be automated After the implementation, there is no need to go to the office one by one to apply. The result after execution is as follows:

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


4. Application of chain of responsibility model in source code

When it comes to the chain of responsibility mode, the most famous one is of course the filter in Servlet . In this system of interceptors and filters , the chain of responsibility mode is used to process each request in turn. First, let’s look at the use of filter. Way. The Filter interface is as follows:

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 is a chain of responsibility of Filter, and its code is as follows:
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);
                }

            }

        }
    }
}


  • In the internalDoFilter() method, you can see that the entire FilterChain uses the array filters to store each filter and its configuration, and uses pos to record which filter is currently traversed, and then executes the obtained Filter 's doFilter method. Different from the linked list method mentioned above, the links here are stored in an array.
  • chain of responsibility pattern in spring

The Interceptor in SpringMVC also uses the Chain of Responsibility pattern . First look at the abstract processing class of 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 {
    }
}



In the abstract processing class, three methods are defined, which are processing pre-processor, post-processor and processing after the entire process is completed. Interceptors are connected in series through HandlerExecutionChain . In HandlerExecutionChain , we need to pay attention to three methods: applyPreHandle , applyPostHandle and triggerAfterCompletion . These three methods respectively execute the preHandle , postHandle and afterCompletion methods defined in the interceptor . And it can also be seen from the code that, like the previous filter, all interceptors are stored in the interceptors array, and the corresponding methods are executed in sequence by traversing the interceptors array in the three methods

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

The responsibility chain mode is also a common design mode. Processors with different responsibilities are connected in series, and the processing method of each processor can be executed through one request. The sender of the request in this way only needs to send a request and does not need to know the detailed link structure; while the receiver of the request only cares about its own processing logic, and passes the request to the next receiver after the processing is completed, thus Complete your own tasks, thus realizing the decoupling of the request sender and receiver. From the source code analysis, we can see that although the linked list structure is commonly used in the chain of responsibility mode, the requirements can also be fulfilled by using arrays and lists.

Author: Jingdong Technology Song Huichao

Source: JD Cloud Developer Community

 

The Indian Ministry of Defense self-developed Maya OS, fully replacing Windows Redis 7.2.0, and the most far-reaching version 7-Zip official website was identified as a malicious website by Baidu. Go 2 will never bring destructive changes to Go 1. Xiaomi released CyberDog 2, More than 80% open source rate ChatGPT daily cost of about 700,000 US dollars, OpenAI may be on the verge of bankruptcy Meditation software will be listed, founded by "China's first Linux person" Apache Doris 2.0.0 version officially released: blind test performance 10 times improved, More unified and diverse extremely fast analysis experience The first version of the Linux kernel (v0.01) open source code interpretation Chrome 116 is officially released
{{o.name}}
{{m.name}}

Guess you like

Origin my.oschina.net/u/4090830/blog/10098350