Has come to the responsibility chain mode, each guest officer listen to me nonsense……

What is the chain of responsibility model

Chain of responsibility mode is a design mode. In the chain of responsibility pattern, many objects are connected in a chain by each object’s reference to its next parent. Requests pass along the chain until one of the objects on the chain decides to process the request. The client making the request does not know which object on the chain ultimately handles the request, allowing the system to dynamically reorganize and assign responsibilities without affecting the client. (Baidu Encyclopedia)

Chain of responsibility mode is a behavioral design mode, which focuses on processing data. Suppose we have a piece of data that needs to be processed by many nodes, it will look like the following:

After a node is processed, it will be handed over to the next node. I don’t know if you have used the approval flow before. When we submit an approval form, your leader will approve it. They are on the same chain. If your leader does not complete the approval, the following nodes cannot receive the information. If your leader rejects your application, the data will not reach the later approval node.

If you’ve worked with the front end, JS will generate bubbling events when clicking on A div, i.e. clicking on A below, A inside B, B inside C, and A-> B -> C will receive the click events in sequence:

For example, in SpringMVC, we sometimes define interceptors to preprocess the request, which means that as the request comes in, it goes through the interceptor, and then it goes into our processing business logic code.

Before, when doing personnel management, there was a process involving personnel dimission, such as handing over work, revoking authority and disabling account, etc. The whole process was very suitable to use the chain of responsibility to deal with. Of course, the automatic processing process is subject to error, save the state of each stage, for the scenario of error, you can manually go to the place where the chain of responsibility is disconnected from the execution. The framework of the whole process is the application of the chain of responsibility, but many other things are added according to the actual situation.

Two problems

  1. Does each node in the chain of responsibility necessarily contain a reference to the next node?

A: Not necessarily. Either put all responsible nodes in a list and deal with them one by one. Either each node contains a reference to the next responsible node,

  1. Is the chain of responsibility impermissible or impermissible?

A: You can use either. You don’t get bogged down in details. You can use it in your own scenario.

Roles in the chain of responsibility model

The chain of responsibility generally has the following roles:

  • Client(Client) : Calls the handling method of the responsibility chain handler, or in the first chain objecthandleMethods.
  • HandlerProcessor: Abstract class that the actual processor inherits and then implementshandleMethod to process the request
  • ConcreteHandler(processor specific) : implementationhandlerClass at the same timehandleMethod, which is responsible for handling business logic classes that vary by business moduleConcreteHandler.
  • HandlerChain: Is responsible for all nodes of the composite responsibility chain and the process (if the node contains a reference to the next node, thenHandlerChainCan not exist)

Implementation of approval chain

Let’s write it differently. Suppose there is a scene now, Qin Huai joined a company, he worked for a year, but he didn’t adjust his salary. After another year, he had to raise his salary.

Uninterrupted mode

To demonstrate the uninterruptible mode, we need to create an entity for the order, which contains the name of the order and the applicant:

public class Requisition {
    / / name
    public String name;


    / / applicant
    public String applicant;


    public Requisition(String name, String applicant) {
        this.name = name;
        this.applicant = applicant; }}Copy the code

Each responsibility node in the responsibility chain, the processor, can be abstracted as an interface:

public interface Handler {
    // Process the application form
    void process(Requisition requisition);
}
Copy the code

We successively realized three different responsibility nodes, representing the approval of leader, director and HR respectively:

public class ManagerHandler implements Handler {
    @Override
    public void process(Requisition requisition) {
        System.out.println(String.format("Manager approves requests from [%s] [%s]...", requisition.applicant, requisition.name)); }}Copy the code
public class DirectorHandler implements Handler{
    @Override
    public void process(Requisition requisition) {
        System.out.println(String.format("Director approves applications from [%s] [%s]...", requisition.applicant, requisition.name)); }}Copy the code
public class HrHandler implements Handler{
    @Override
    public void process(Requisition requisition) {
        System.out.println(String.format("Hr approves applications from [%s] [%s]...", requisition.applicant, requisition.name)); }}Copy the code

Now that we have all the responsibility nodes, we need to use a responsibility chain to connect them:

public class HandlerChain {
    List<Handler> handlers = new ArrayList<>();


    public void addHandler(Handler handler){
        handlers.add(handler);
    }


    public void handle(Requisition requisition){
        for(Handler handler:handlers){
            handler.process(requisition);
        }
        System.out.println(String.format("Application form from [%s] has been approved.", requisition.applicant, requisition.name)); }}Copy the code

Client test classes:

public class ClientTest {
    public static void main(String[] args) {
        HandlerChain handlerChain = new HandlerChain();
        handlerChain.addHandler(new ManagerHandler());
        handlerChain.addHandler(new DirectorHandler());
        handlerChain.addHandler(new HrHandler());
        handlerChain.handle(new Requisition("Application for a raise"."Qin Huai")); }}Copy the code

Running results:

Manager approves the application form [salary increase application] from [Qin Huai]... The Director approved the application form from [Qin Huai]... Hr approves the application form [salary increase application] from [Qin Huai]... The application form from [Qin Huai] has been approvedCopy the code

From the point of view of the results, the application form has indeed gone through every node and formed a chain, which is the core idea of the chain of responsibility. Each node gets the same data, the same requisition.

Interrupt mode

Qin Huai’s idea of salary increase is very nice, but the reality is very dull, the above approval process is smooth, but in case Hr wants to reject this application, the above code does not give her this ability, so the code has to be changed! (Hr heart: I want this feature, it will be available tomorrow).

If any node is not approved, it will return directly. It will not go to the next node. First add the return value to the abstract processing node method:

public interface Handler {
    // Process the application form
    boolean process(Requisition requisition);
}
Copy the code

The three processing nodes are also modified synchronously:

public class ManagerHandler implements Handler {
    @Override
    public boolean process(Requisition requisition) {
        System.out.println(String.format("Manager approves request form [%s] from [%s]...", requisition.applicant, requisition.name));
        return true; }}Copy the code
public class DirectorHandler implements Handler{
    @Override
    public boolean process(Requisition requisition) {
        System.out.println(String.format("Director approves application form from [%s] [%s]...", requisition.applicant, requisition.name));
        return true; }}Copy the code
public class HrHandler implements Handler{
    @Override
    public boolean process(Requisition requisition) {
        System.out.println(String.format("Hr does not approve application form [%s] from [%s]...", requisition.applicant, requisition.name));
        return false; }}Copy the code

Processing chain adjustment:

public class HandlerChain {
    List<Handler> handlers = new ArrayList<>();


    public void addHandler(Handler handler) {
        handlers.add(handler);
    }


    public void handle(Requisition requisition) {
        for (Handler handler : handlers) {
            if(! handler.process(requisition)) { System.out.println(String.format("Application form [%s] from [%s] failed to be approved", requisition.applicant, requisition.name));
                return;
            }
        }
        System.out.println(String.format("Application form from [%s] has been approved.", requisition.applicant, requisition.name)); }}Copy the code

Result after modification:

Manager approves the application form [salary increase application] from [Qin Huai]... Director approved the application form [salary increase application] from [Qin Huai]... Hr did not approve the application form from [Qin Huai]... The application form [salary increase application] from [Qin Huai] was not approvedCopy the code

Qin huai cried, the approval of the raise was rejected by HR. Despite the rejection, Qin huai also felt the chain of responsibility pattern that can be broken, which is also common when dealing with requests, because we don’t want illegal requests to fall into the normal processing logic.

Contains a reference to the next node

As mentioned earlier, ** In the chain of responsibility pattern, many objects are connected in a chain by each object’s reference to its next parent. ** All of the above are written without reference to the next node. Let’s practice how to complete the chain of responsibility using reference writing.

Alter Handler interface to abstract class:

public abstract class Handler {

    private Handler nextHandler;

    public void setNextHandler(Handler handler) {
        this.nextHandler = handler;
    }

    // Process the application form
    protected abstract boolean process(Requisition requisition);

    // Method of exposure
    public boolean handle(Requisition requisition) {
        boolean result = process(requisition);
        if (result) {
            if(nextHandler ! =null) {
                return nextHandler.handle(requisition);
            } else {
                return true; }}return false; }}Copy the code

Three implementation classes remain unchanged:

public class ManagerHandler extends Handler{
    @Override
    boolean process(Requisition requisition) {
        System.out.println(String.format(
                "Manager approves request form [%s] from [%s]...", requisition.applicant, requisition.name));
        return true; }}public class DirectorHandler extends Handler {
    @Override
    public boolean process(Requisition requisition) {
        System.out.println(String.format(
                "Director approves application form from [%s] [%s]...", requisition.applicant, requisition.name));
        return true; }}public class HrHandler extends Handler{
    @Override
    public boolean process(Requisition requisition) {
        System.out.println(String.format("Hr does not approve application form [%s] from [%s]...",
                requisition.applicant, requisition.name));
        return false; }}Copy the code

Test method to construct nested references:

public class ClientTest {
    public static void main(String[] args) {
        HrHandler hrHandler = new HrHandler();
        DirectorHandler directorHandler = new DirectorHandler();
        directorHandler.setNextHandler(hrHandler);
        ManagerHandler managerHandler = new ManagerHandler();
        managerHandler.setNextHandler(directorHandler);

        managerHandler.handle(new Requisition("Application for a raise"."Qin Huai")); }}Copy the code

You can see the same result:

Manager approves the application form [salary increase application] from [Qin Huai]... Director approved the application form [salary increase application] from [Qin Huai]... Hr did not approve the application form from [Qin Huai]...Copy the code

Expand the

In fact, the responsibility chain is more convenient with Spring, mainly for two reasons:

You can use injection to automatically identify all implementation classes of the interface.

@Autowire
public List<Handler> handlers;
Copy the code

2. You can use the @Order annotation to make the interface implementation classes execute sequentially.

@Order(1)
public class HrHandler extends Handler{... }Copy the code

Application in source code

  • MybatisIn thePluginThe mechanism uses the chain of responsibility pattern, with various official or custom configurationsPlugin, andFilterSimilarly, it can be executedSqlStatement to perform some operations.
  • SpringThe chain of responsibility model is used to manageAdviser.

For example, several plug-ins can be added in Mybatis, such as PageHelper. Multiple plug-ins use dynamic proxy to realize the object packaging, and multi-level proxy.

// Responsibility chain plug-in
public class InterceptorChain {

  private final List<Interceptor> interceptors = new ArrayList<>();
  // Generate a proxy object
  public Object pluginAll(Object target) {
    for (Interceptor interceptor : interceptors) {
      target = interceptor.plugin(target);
    }
    return target;
  }
  // Layer upon layer of interceptors
  public void addInterceptor(Interceptor interceptor) {
    interceptors.add(interceptor);
  }

  public List<Interceptor> getInterceptors(a) {
    returnCollections.unmodifiableList(interceptors); }}Copy the code

conclusion

Advantages of the chain of responsibility model:

  • Reduce the direct coupling of an object, and the object is automatically passed to the next responsible node, either by reference or by non-reference.
  • Enhance the scalability, if you need to add new responsibility nodes, it is also more convenient to achieve a specific interface.
  • The order of responsibility nodes is controllable. You can specify an order attribute and sort it.
  • Each responsibility node has a specific responsibility and only handles its own task, in line with the single responsibility principle of the class.

Disadvantages of the chain of responsibility:

  • If the chain of responsibility is long, performance will suffer.
  • The chain of responsibility can be broken in the middle and requests may not necessarily be received.

The chain of responsibility is generally in process processing, multiple nodes process the same data and transfer them sequentially, which may have sequential requirements or may not. The capability of the processor is abstracted into an interface, which is convenient for expansion.

Design Mode series:

  • Design Patterns [1] — How many ways to write singleton patterns?
  • Design Patterns [1.1] — How do you want to break the singleton pattern?
  • Design Patterns [1.2] — Are enumerated singletons so useful?
  • Design Patterns [1.3] — Why are Hungry singletons thread-safe?
  • Design Patterns [2] — Simple factory patterns?
  • Design Patterns [2.1] — How does the simple factory pattern evolve into the Factory Method Pattern?
  • Design Patterns [2.2] — How did factory patterns evolve into abstract factory patterns?
  • Design mode [3.1] — A brief discussion on the static, dynamic and CGLIB agents of agent mode
  • Design Pattern [3.2] – How interesting is JDK dynamic proxy source code analysis?
  • Design Pattern [3.3] — CGLIB dynamic proxy source code interpretation
  • Design Pattern [4] — Detailed explanation of builder’s pattern
  • Design Pattern [5] — Prototype pattern
  • Design Patterns [6.1] — Explore adapter patterns
  • Design patterns [6.2] — Let’s talk more about adapter patterns
  • Design Patterns [7] — Explore bridging patterns
  • Design Pattern [8] — Manual Geng taught me to write decorative pattern
  • Design Patterns [9] — Appearance patterns? It’s not that glamorous
  • Design Patterns [10] — Take a look at the Share patterns
  • Design patterns [11] — Fix combination patterns
  • Design Mode [12] — Strategy mode for overcoming the recent fire
  • Design Mode [13] — How about template mode?
  • Design Patterns [14] — Learn command patterns from smart speakers

[Author profile] : Qin Huai, public number [Qin Huai grocery store] author, personal website: aphysia.cn, the road of technology is not at that time, mountain high water long, even if slow, chi and not stop.

Offer all questions in PDF

Open Source Programming Notes