Design mode [15] - learn the responsibility chain mode from the approval process

I've come to the responsibility chain mode. My guest, listen to my nonsense

What is the responsibility chain model

Responsibility chain model is a design model. In the responsibility chain model, many objects are connected by each object's reference to its next family to form a chain. Requests are passed along the chain until an object in the chain decides to process the request. The client sending the request does not know which object in the chain will eventually process the request, which makes the system dynamically reorganize and allocate responsibilities without affecting the client. (Baidu Encyclopedia)

The responsibility chain model is a behavioral design model, which focuses on data processing. Assuming that we have a data that needs to be processed by many nodes, it will look like the following:

After a node is processed, it is handed over to the next node. I don't know whether you have used the approval process. After we submit an approval form, your leader approves it. After the leader approves it, the director approves it. The director may be followed by senior director, cto or hr. They are on the same chain. If your leader has not been approved, the subsequent nodes will not receive information. If your leader rejects your application, the data will not reach the subsequent approval node.

If you have touched the front end, a bubble event will occur when you click a div in JS, that is, click a below, a is in B, B is in C, and a - > b - > C will receive click events in turn:

For another example, in spring MVC, we sometimes define some interceptors to preprocess the request, that is, when the request comes, it will go through the interceptors in turn. After passing through the interceptors, it will enter our processing business logic code.

Previously, in the process of personnel management, there was a processing process involving the resignation of personnel, such as handing over work, releasing permissions, disabling accounts, etc. this whole processing process is very suitable for using the responsibility chain. Of course, there will be errors in the automatic processing process. Save the state of each stage. For the error scenario, you can manually execute it from the place where the responsibility chain is disconnected. The framework of the whole process is to apply the responsibility chain, but many other things are added according to the actual scenario.

Two questions

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

A: not necessarily. Either put all responsibility nodes in a list and deal with them in turn; Either each node contains a reference to the next responsible node,

  1. Is the responsibility chain not allowed to be interrupted or not?

A: both are OK. They don't stick to details and can be used according to their own scenes.

Roles in the responsibility chain model

The responsibility chain generally has the following roles:

  • Client (client): invoke the handling method of the responsible chain processor, or call the handle method in the first chain object.
  • Handler: an abstract class, which is provided to the actual processor to inherit, and then implement the handle method to process the request
  • ConcreteHandler (concrete processor): it implements the class of handler and the handle method. It is responsible for processing the business logic class. Different business modules have different concretehandlers.
  • HandlerChain: responsible for combining all nodes and processes of the responsibility chain (if the node contains a reference to the next node, HandlerChain may not exist)

Implementation of approval chain

Now let's write different ways. Suppose there is a scene now. Qin Huai joined a company and worked for a year, but he hasn't adjusted his salary. After another year, he has to get a raise. If he doesn't, he will run away with the bucket. So Qin Huai boldly went to the internal system and submitted an application form: [salary increase application]

Uninterrupted mode

To demonstrate the uninterrupted mode first, you have to get an entity of the application form, which contains the name of the application form 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;
    }
}

Each responsibility node in the responsibility chain, that is, the processor, can be abstracted into an interface:

public interface Handler {
    // Processing application form
    void process(Requisition requisition);
}

We have implemented three different responsibility nodes in turn, representing leader, director and hr for approval:

public class ManagerHandler implements Handler {
    @Override
    public void process(Requisition requisition) {
        System.out.println(String.format("Manager Approval from[%s]Application form for[%s]...", requisition.applicant, requisition.name));
    }
}
public class DirectorHandler implements Handler{
    @Override
    public void process(Requisition requisition) {
        System.out.println(String.format("Director Approval from[%s]Application form for[%s]...", requisition.applicant, requisition.name));
    }
}
public class HrHandler implements Handler{
    @Override
    public void process(Requisition requisition) {
        System.out.println(String.format("Hr Approval from[%s]Application form for[%s]...", requisition.applicant, requisition.name));
    }
}

There are all responsibility nodes. We need to combine them with a responsibility chain:

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("come from[%s]Application form for[%s]Approval completed", requisition.applicant, requisition.name));
    }
}

Client test class:

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("Salary increase application","Qin Huai"));
    }
}

Operation results:

Manager Approval from[Qin Huai]Application form for[Salary increase application]...
Director Approval from[Qin Huai]Application form for[Salary increase application]...
Hr Approval from[Qin Huai]Application form for[Salary increase application]...
come from[Qin Huai]Application form for[Salary increase application]Approval completed

From the result, the application form has indeed experienced every node and formed a chain, which is the core idea of the responsibility chain. Each node gets the same data and the same application form.

Interrupt mode

Qin Huai's idea of salary increase is very beautiful, but the reality is very skinny. The above approval process is smooth. However, in case Hr wants to reject this application form, the above code does not give her this ability. Therefore, the code must be changed! (Hr heart: I want this function, which will be launched tomorrow).

Since it supports interruption, that is, it supports any node to return directly if it fails to pass the approval. It will not go to the next node. First add the return value to the abstract processing node method:

public interface Handler {
    // Processing application form
    boolean process(Requisition requisition);
}

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 Approved from[%s]Application form for[%s]...", requisition.applicant, requisition.name));
        return true;
    }
}
public class DirectorHandler implements Handler{
    @Override
    public boolean process(Requisition requisition) {
        System.out.println(String.format("Director Approved from[%s]Application form for[%s]...", requisition.applicant, requisition.name));
        return true;
    }
}
public class HrHandler implements Handler{
    @Override
    public boolean process(Requisition requisition) {
        System.out.println(String.format("Hr Approval failed from[%s]Application form for[%s]...", requisition.applicant, requisition.name));
        return false;
    }
}

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("come from[%s]Application form for[%s]Approval failed", requisition.applicant, requisition.name));
                return;
            }
        }
        System.out.println(String.format("come from[%s]Application form for[%s]Approval completed", requisition.applicant, requisition.name));
    }
}

Results after modification:

Manager Approved from[Qin Huai]Application form for[Salary increase application]...
Director Approved from[Qin Huai]Application form for[Salary increase application]...
Hr Approval failed from[Qin Huai]Application form for[Salary increase application]...
come from[Qin Huai]Application form for[Salary increase application]Approval failed

Qin Huai cried and the approval of the salary increase was hr rejected. Although rejected, Qin Huai also felt the interruptible responsibility chain mode. This writing is also common when processing requests, because we don't want illegal requests to be included in the normal processing logic.

Contains a reference to the next node

As mentioned earlier, * * in the responsibility chain mode, many objects are connected by each object's reference to its next family to form a chain** All the above expressions do not contain the reference of the next node. Let's practice how to use reference to complete the responsibility chain.

Transform the Handler interface into an abstract class:

public abstract class Handler {

    private Handler nextHandler;

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

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

    // Exposure method
    public boolean handle(Requisition requisition) {
        boolean result = process(requisition);
        if (result) {
            if (nextHandler != null) {
                return nextHandler.handle(requisition);
            } else {
                return true;
            }
        }
        return false;
    }
}

Three implementation classes remain unchanged:

public class ManagerHandler extends Handler{
    @Override
    boolean process(Requisition requisition) {
        System.out.println(String.format(
                "Manager Approved from[%s]Application form for[%s]...", requisition.applicant, requisition.name));
        return true;
    }
}
public class DirectorHandler extends Handler {
    @Override
    public boolean process(Requisition requisition) {
        System.out.println(String.format(
                "Director Approved from[%s]Application form for[%s]...", requisition.applicant, requisition.name));
        return true;
    }
}

public class HrHandler extends Handler{
    @Override
    public boolean process(Requisition requisition) {
        System.out.println(String.format("Hr Approval failed from[%s]Application form for[%s]...",
                requisition.applicant, requisition.name));
        return false;
    }
}

Test method, 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("Salary increase application","Qin Huai"));
    }
}

You can see that the running result is the same:

Manager Approved from[Qin Huai]Application form for[Salary increase application]...
Director Approved from[Qin Huai]Application form for[Salary increase application]...
Hr Approval failed from[Qin Huai]Application form for[Salary increase application]...

Expand

In fact, Spring is easier to use with the responsibility chain. There are two main points:

1. Injection can be used to automatically identify all implementation classes of the interface.

@Autowire
public List<Handler> handlers;

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

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

Application in source code

  • The Plugin mechanism in Mybatis uses the responsibility chain mode to configure various official or custom plugins. Similar to Filter, it can perform some operations when executing Sql statements.
  • Spring uses the responsibility chain pattern to manage the Adviser.

For example, several plug-ins can be added to Mybatis, such as PageHelper. Multiple plug-ins use dynamic agents to wrap objects, and multi-layer agents.

//Responsibility chain plug-in
public class InterceptorChain {

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

  public List<Interceptor> getInterceptors() {
    return Collections.unmodifiableList(interceptors);
  }

}

summary

Advantages of the responsibility chain model:

  • Reduce the direct coupling of the object, and the object will be automatically transferred to the next responsibility node, whether by reference or non reference.
  • Enhance expansibility. If you need to add a new responsibility node, it is also more convenient to implement a specific interface.
  • The order of responsibility nodes can be controlled. You can specify an order attribute to sort.
  • Each responsibility node has specific responsibilities and only handles its own tasks, which conforms to the principle of single responsibility of the class.

Disadvantages of responsibility chain:

  • If the responsibility chain is long, the performance will be affected.
  • The chain of responsibility may break halfway, and the request may not be received.

The responsibility chain is generally in the process of processing. Multiple nodes process the same data and transfer it in turn. There may or may not be sequential requirements. The ability of the processor is abstracted into an interface to facilitate expansion.

Design pattern series:

[about the author]:
Qin Huai, the official account of Qin Huai grocery store, author's personal website: http://aphysia.cn , the road of technology is not for a while, with high mountains and long rivers. Even if it is slow, it will gallop without stopping.

Sword finger Offer all questions PDF

Open source programming notes

Keywords: Java Front-end Design Pattern

Added by Birdfeed on Wed, 16 Feb 2022 03:16:17 +0200