Follow the public account JavaStorm easy to read and receive the latest dry goods

Chain of Responsibility model

define

Chain of Responsibility Pattern: Avoid coupling the sender of a request with the receiver, make it possible for multiple objects to receive a request, connect these objects into a Chain, and pass the request along the Chain until an object handles it. The responsibility chain pattern is an object behavior pattern.

In common sense, we try to pass the buck on something, we go to A, A kicks the ball, says it’s none of my business, goes to B, and we go to B, and B says, it’s none of my business, go to C, and that’s it, we get kicked around, that’s the idea of the chain of responsibility model.

role

Handler (Abstract Handler) : It defines the interface for handling requests. It is typically designed as an abstract class, holding a reference to a Handler and passing it on to the next Handler if it cannot handle the request itself. Therefore, an object of type abstract handler is defined in the abstract handler as its reference to the next handler. With this reference, the handler can be linked into a chain.

ConcreteHandler: It is a subclass of abstract handler, which can process user requests. In the concrete handler class, the abstract request processing method defined in the abstract handler is realized. Before processing the request, we need to judge whether there is a corresponding processing permission. The next object in the chain can be accessed in the concrete handler for forwarding the request.

Pure chain of responsibility model

  • A specific processing object can choose only one of the processors: it can either assume full responsibility or pass the processing responsibility to the next processor, without allowing one processor to process part of it and then pass it on.
  • A request must be received by a handler object, and no request can be processed by any handler object

Impure chain of responsibility model

  • Allows a request to be processed by a specific handler before being passed down
  • After a specific handler has processed a request, it can continue processing the request.
  • A request can end up not being processed by any processor.

Code sample

Pure chain of responsibility model

Now simulate a scenario request that can be processed if the number matches an odd number, specifies a number, some lower limit, and so on, or fails to be processed otherwise.

First, we define the abstract handler, along with a processing method template that defines which requests can be processed and which can’t be passed to Next.

package com.zero.headfirst.chain.support;

import java.util.Objects;

/** * Abstract handler: Defines an abstract request handler that holds a reference to an abstract handler used to generate a processing chain */
public abstract class AbstractSupport {
    private String name;
    private AbstractSupport next;

    /** * defines the algorithm template for processing requests */
    public final void process(NumberData numberData) {
        if (resolve(numberData)) {
            done(numberData);
        } else if (Objects.nonNull(next)) {
            next.process(numberData);
        } else{ fail(numberData); }}/** * cannot handle *@param numberData
     */
    protected  void fail(NumberData numberData) {
        System.out.println(numberData + " cannot be resolved.");
    }

    /** * Successfully processed *@param numberData
     */
    protected void done(NumberData numberData) {
        System.out.println(numberData + " is resolved by " + this + ".");
    }

    /** * subclass overrides * to see if it can handle@param numberData
     * @return* /
    protected abstract boolean resolve(NumberData numberData);

    public AbstractSupport setNext(AbstractSupport next) {
        this.next = next;
        return next;
    }

    public AbstractSupport(String name) {
        this.name = name;
    }

    @Override
    public String toString(a) {
        return "AbstractSupport{" +
                "name='" + name + '\' ' +
                '} '; }}Copy the code

Then define our specific handler, NoSupport is a class that never resolves, just passes the buck.

package com.zero.headfirst.chain.support;

/** * is never resolved, return false */
public class NoSupport extends AbstractSupport {
    public NoSupport(String name) {
        super(name);
    }

    @Override
    protected boolean resolve(NumberData numberData) {
        return false; }}Copy the code

LimitSupport resolves requests for a specified scope

package com.zero.headfirst.chain.support;

/** * resolves the specified interval */
public class LimitSupport extends AbstractSupport {

    private Integer limit;

    public LimitSupport(String name, Integer limit) {
        super(name);
        this.limit = limit;
    }

    @Override
    protected boolean resolve(NumberData numberData) {
        returnnumberData.getNumber() < limit; }}Copy the code

OddSupport class, to solve the problem of odd numbers

package com.zero.headfirst.chain.support;

/** * odd number processing */
public class OddSupport extends AbstractSupport {
    public OddSupport(String name) {
        super(name);
    }

    @Override
    protected boolean resolve(NumberData numberData) {
        return numberData.getNumber() % 2= =1; }}Copy the code

SpecialSupport specifies the number processor

package com.zero.headfirst.chain.support;

/** * specifies the number of special processing */
public class SpecialSupport extends AbstractSupport {
   private Integer number;

    public SpecialSupport(String name, Integer number) {
        super(name);
        this.number = number;
    }

    @Override
    protected boolean resolve(NumberData numberData) {
        returnnumberData.getNumber().equals(number); }}Copy the code

Finally, we define a client to assemble our chain of responsibilities, link all the processors together, and simulate requests

package com.zero.headfirst.chain.support;

public class ChainTest {
    public static void main(String[] args) {
        AbstractSupport alice = new NoSupport("Alice");
        AbstractSupport jams = new OddSupport("jams");
        AbstractSupport bob = new LimitSupport("Bob".100);
        AbstractSupport charlie = new SpecialSupport("Charlie".429);

        // Define the chain of responsibility
        alice.setNext(jams).setNext(bob).setNext(charlie);
        for (int i = 0; i < 500; i += 33) {
            alice.process(newNumberData(i)); }}}Copy the code

Our print result: except for the number that we define the processor can process, other numbers cannot be processed.

NumberData{number=0} is resolved by AbstractSupport{name='Bob'}.
NumberData{number=33} is resolved by AbstractSupport{name='jams'}.
NumberData{number=66} is resolved by AbstractSupport{name='Bob'}.
NumberData{number=99} is resolved by AbstractSupport{name='jams'}.
NumberData{number=132} cannot be resolved.
NumberData{number=165} is resolved by AbstractSupport{name='jams'}.
NumberData{number=198} cannot be resolved.
NumberData{number=231} is resolved by AbstractSupport{name='jams'}.
NumberData{number=264} cannot be resolved.
NumberData{number=297} is resolved by AbstractSupport{name='jams'}.
NumberData{number=330} cannot be resolved.
NumberData{number=363} is resolved by AbstractSupport{name='jams'}.
NumberData{number=396} cannot be resolved.
NumberData{number=429} is resolved by AbstractSupport{name='jams'}.
NumberData{number=462} cannot be resolved.
NumberData{number=495} is resolved by AbstractSupport{name='jams'}.
Copy the code

Impure chain of responsibility model

For example, the company needs to approve the leave. For example, if the leave is less than 3 days, the supervisor will approve it. 4-10 days, manager approval; 11-30 days, general manager approval; More than 30 days, not approved, etc. This is a step by step decision. If we ignore design patterns, we can use if… The else… I solved it, but the problem, as you can imagine, is far more complicated in practice than in this example.

Now we solve this with the chain of responsibility design pattern, connecting each role to process the leave data flow.

Abstract processor

package com.zero.design.actions.dutychain;

/** * Responsibility chain abstract class, containing core handling methods, and subsequent responsibility handler Settings. Inherited by different processors */
public abstract class AbstractLeaderHandler {

    protected String handlerName;

    /** * A successor in the chain of responsibility, that is, this object cannot be handled and passes to the next Leader */
    protected AbstractLeaderHandler nextLeaderHandler;

    public AbstractLeaderHandler(String handlerName) {
        this.handlerName = handlerName;
    }

    /** * The core business method that handles the request * needs to be implemented by the different processors that inherit the class *@param request
     */
    public abstract void handleRequest(LeaveRequest request);

    /** * Sets the successor in the responsibility chain *@param nextLeaderHandler
     */
    public AbstractLeaderHandler setNextLeaderHandler(AbstractLeaderHandler nextLeaderHandler) {
        this.nextLeaderHandler = nextLeaderHandler;
        returnnextLeaderHandler; }}Copy the code

Specific processor – Master processor

Responsible for dealing with holidays of less than 3 days, more than 3 days to submit to supervisor

package com.zero.design.actions.dutychain;

/** * The main processor, inherits the abstract processor interface, handles the corresponding business or sets up the next processor */
public class DirectorHandler extends AbstractLeaderHandler {


    public DirectorHandler(String handlerName) {
        super(handlerName);
    }

    @Override
    public void handleRequest(LeaveRequest request) {
        int days = request.getLeaveDays(); // Get the days of leave
        String name = request.getName(); // Get the name of the person asking for leave
        String reason = request.getReason(); // Get the reason for asking for leave

        if (days <= 3) {
            // If the requirements are met within 3 days, the director will approve directly
            System.out.println("Employees" + name + "Leave" + days + "God, reason:" + reason);
            System.out.println("Director" + this.handlerName + "Approved");
        } else {
            System.out.println("Too many days off, Chief." + this.handlerName + "When it's done, it's over to the manager.");
            if(this.nextLeaderHandler ! =null) {
                Otherwise, if the next Leader exists on the chain, let him handle it
                this.nextLeaderHandler.handleRequest(request); }}}}Copy the code

Manager processor

Handle leave less than 10 days, and submit more than 10 days to general office

package com.zero.design.actions.dutychain;

/** * Manager processor */
public class ManagerHandler extends AbstractLeaderHandler {

    public ManagerHandler(String handlerName) {
        super(handlerName);
    }

    @Override
    public void handleRequest(LeaveRequest request) {
        int days = request.getLeaveDays(); // Get the days of leave
        String name = request.getName(); // Get the name of the person asking for leave
        String reason = request.getReason(); // Get the reason for asking for leave

        if(days <= 10) { // If the requirements within 10 days are met, the manager will approve directly
            System.out.println("Employees" + name + "Leave" + days + "God, reason:" + reason);
            System.out.println("Manager" + this.handlerName + "Approved");
        } else {
            System.out.println("Too many days off, manager." + this.handlerName + "The processing is over, and the general office will handle it.");
            if(this.nextLeaderHandler ! =null) { Otherwise, if the next Leader exists on the chain, let him handle it
                this.nextLeaderHandler.handleRequest(request); }}}}Copy the code

General office processor

Process requests for holidays greater than 10 days

package com.zero.design.actions.dutychain;

/** * General office processor */
public class GeneralManagerHandler extends AbstractLeaderHandler {

    public GeneralManagerHandler(String handlerName) {
        super(handlerName);
    }

    @Override
    public void handleRequest(LeaveRequest request) {
        int days = request.getLeaveDays(); // Get the days of leave
        String name = request.getName(); // Get the name of the person asking for leave
        String reason = request.getReason(); // Get the reason for asking for leave

        if(days <= 30) { // If the requirements within 10 days are met, the manager will approve directly
            System.out.println("Employees" + name + "Leave" + days + "God, reason:" + reason);
            System.out.println("Agent" + this.handlerName + "Approved");
        } else {
            System.out.println("Too many days off, general office." + this.handlerName + "Can't handle it.");
            if(this.nextLeaderHandler ! =null) { Otherwise, if the next Leader exists on the chain, let him handle it
                this.nextLeaderHandler.handleRequest(request);
            } else {
                System.out.println("Leave of absence unsuccessful"); }}}}Copy the code

Client Role

Declare a processing chain that simulates the leave data flow

package com.zero.design.actions.dutychain;

/** * mock requests: * They process requests under different conditions, and pass requests to the next person on the chain for processing as long as they are not within their scope */
public class Client {

    public static void main(String[] args) {
        // To initialize the chain of responsibility, use singletons instead
        AbstractLeaderHandler directorHandler = new DirectorHandler("Zhang Shengnan");
        AbstractLeaderHandler managerHandler = new ManagerHandler("Bill");
        AbstractLeaderHandler generalManagerHndler = new GeneralManagerHandler("Prince of zhao");

        // Organize the relationship between responsibility objects
        directorHandler.setNextLeaderHandler(managerHandler).setNextLeaderHandler(generalManagerHndler);
        // Start asking for leave
        LeaveRequest request = new LeaveRequest("Ni Shengwu".15."Sleep at home"); directorHandler.handleRequest(request); }}Copy the code

Finally, we look at the print

Too many days of leave, director Zhang Shengnan to deal with the end, but also to the manager to deal with too many days of leave, manager Li Si to deal with the end, to the general office to deal with the employee Ni Shengwu leave for 15 days, reason: sleep at home, general office Zhao Wang approvedCopy the code

conclusion

Advantages of the chain of responsibility model

  • Objects only need to know that the request will be processed, and objects in the chain do not need to know the structure of the chain, the client is responsible for the creation of the chain, reducing the coupling degree of the system
  • The request-processing object maintains only one reference to its successors, rather than all its references to candidate handlers, simplifying the interconnections of objects
  • A responsibility chain gives us more flexibility in assigning responsibilities to objects, adding, deleting and changing the chain dynamically at run time to change the responsibility for handling a request
  • When you add a new concrete request handler, you don’t need to modify the original code, you just need to create a new chain on the client side, in accordance with the “open close principle”.

disadvantages

  • A request may not be processed because the chain of responsibility is not configured correctly
  • For a long chain of responsibilities, the processing of the request may involve multiple processing objects, which will affect the system performance and make it inconvenient to debug
  • The system may fall into an infinite loop due to improper creation of the responsibility chain

Usage scenarios

  1. In the NettyPipelineChannelHandlerOrganize code logic through the chain of responsibility design pattern
  2. Responsibility chain pattern in the Tomcat filter.
  3. Plugin mechanism in Mybatis uses the responsibility chain mode to configure various official or custom plugins, similar to Filter, which can perform some operations when executing Sql statements
  • There are multiple objects that can handle the same request, and which object will handle the request will be determined at run-time. The client just needs to submit the request to the chain and doesn’t care who the request is handled by or how it is handled
  • Submit a request to one of multiple objects without explicitly specifying the recipient
  • You can dynamically specify a set of objects to handle requests, and clients can dynamically create chains of responsibilities to handle requests and change the order of the handlers in the chain