Responsibility chain model of behavior model

1 General

Chain of responsibility pattern is a common behavior pattern. It encapsulates the processor into a processing chain, so that the request can be delivered on the chain. The processor on the chain decides whether to process the request or not.

2 responsibility chain mode

A typical usage scenario of responsibility chain mode is when an event or request needs to be processed by multiple processors. Apply the responsibility chain mode, string all processors together, and then transfer the request from the head of the chain. Each processor can judge the request, choose to execute the relevant logic, or pass it to the next processor. In this way, the requester and receiver (all processors) are decoupled. Generally, the processor in the chain needs to have the reference of the next processor continuously in order to send the request back. In addition, a single processor does not need to know the overall structure, but only needs to focus on its own internal processing logic. The addition, deletion and modification of the processor are also very convenient. Only the corresponding processing on the chain is needed.

3 cases

In reality, there are many cases of chain processing, such as interview process. A candidate needs to go through multiple rounds of interviews and be qualified before entering a new company. The interviewer here, the processor, is used to process the interview request. At the same time, a company has interviewers in multiple fields, Java interviewers, who only choose to interview java candidates; HR will interview all candidates:

public enum Major {
    JAVA, PYTHON, NA
}
// Interview candidates
public class Interviewee {
    private String name;
    private Major major;
    public Interviewee(String name, Major major) {
        this.name = name;
        this.major = major;
    }
    public String getName() { return name; }
    public Major getMajor() { return major; }
    [@Override](https://my.oschina.net/u/1162528)
    public String toString() {
        return "Interviewee " + name + ", majors in " + major;
    }
}

// Interviewers, subclasses need to implement their own interview methods
public abstract class Interviewer {
    private Major major;
    protected Interviewer nextInterviewer;
    public Interviewer(Major major) {
        this.major = major;
    }
    public Major getMajor() { return major; }
    // Important method: set up the next interviewer and form the responsibility chain
    public void setNext(Interviewer nextInterviewer) {
        this.nextInterviewer = nextInterviewer;
    };
    public abstract boolean interview(Interviewee interviewee);
}
// java interviewer
public class JavaInterviewer extends Interviewer {
    public JavaInterviewer() {
        super(Major.JAVA);
    }
    [@Override](https://my.oschina.net/u/1162528)
    public boolean interview(Interviewee interviewee) {
        if (interviewee.getMajor().equals(this.getMajor())) {
            // Interview only when the field corresponds
            System.out.println(interviewee.getName() + " is on java interview.");
            if (new Random().nextInt(10) <= 8) {// Simulated interview results
                System.out.println(interviewee.getName() + " passed java interview!");
            }
            else {
                System.out.println(interviewee.getName() + " failed on java interview!");
                return false;
            }
        }
        else {
            // If the field doesn't match, pass it directly to the next interviewer
            System.out.println("Java interviewer will not do interview on " + interviewee.getName());
        }
        if (nextInterviewer != null) {
            // Pass it on to the next interviewer
            return nextInterviewer.interview(interviewee);
        }
        return true;
    }
}
// python interviewer
public class PythonInterviewer extends Interviewer {
    public PythonInterviewer() {
        super(Major.PYTHON);
    }
    [@Override](https://my.oschina.net/u/1162528)
    public boolean interview(Interviewee interviewee) {
        if (interviewee.getMajor().equals(this.getMajor())) {
            // Interview only when the field corresponds
            System.out.println(interviewee.getName() + " is on python interview.");
            if (new Random().nextInt(10) <= 8) {// Simulated interview results
                System.out.println(interviewee.getName() + " passed python interview!");
            }
            else {
                System.out.println(interviewee.getName() + " failed on python interview!");
                return false;
            }
        }
        else {
            // If the field doesn't match, pass it directly to the next interviewer
            System.out.println("Python interviewer will not do interview on " + interviewee.getName());
        }
        if (nextInterviewer != null) {
            // Pass it on to the next interviewer
            return nextInterviewer.interview(interviewee);
        }
        return true;
    }
}
// HR interviewer
public class HRInterviewer extends Interviewer {
    public HRInterviewer() {
        super(null);// no major for HR
    }
    [@Override](https://my.oschina.net/u/1162528)
    public boolean interview(Interviewee interviewee) {
        System.out.println(interviewee.getName() + " is on HR interview.");
        if (new Random().nextInt(10) <= 9) {// Simulated interview results
            System.out.println(interviewee.getName() + " passed hr interview!");
        }
        else {
            System.out.println(interviewee.getName() + " failed on hr interview!");
            return false;
        }
        if (nextInterviewer != null) {
            // Pass it on to the next interviewer
            return nextInterviewer.interview(interviewee);
        }
        return true;
    }
}
// Responsibility chain object
public class InterviewProcess {
    // Only the reference of the first interviewer is needed, and the request will be automatically sent back
    Interviewer firstInterviewer;
    public InterviewProcess() {
        // Set up responsibility chain
        Interviewer javaInterviewer = new JavaInterviewer();
        Interviewer pythonInterviewer = new PythonInterviewer();
        Interviewer hrInterviewer = new HRInterviewer();
        javaInterviewer.setNext(pythonInterviewer);
        pythonInterviewer.setNext(hrInterviewer);
        this.firstInterviewer = javaInterviewer;
    }

    public boolean process(Interviewee interviewee) {
        return firstInterviewer.interview(interviewee);
    }
}

public class Test {
    public static void main(String[] args) throws InterruptedException {
        InterviewProcess interviewProcess = new InterviewProcess();
        Interviewee mario = new Interviewee("Mario", Major.JAVA);
        Interviewee link = new Interviewee("Link", Major.PYTHON);

        System.out.println(mario + " is taking interview...");
        boolean result = interviewProcess.process(mario);
        System.out.println("Interview result of " + mario.getName() + ": " + (result ? "pass" : "fail"));
        System.out.println("==================================");
        System.out.println(link + " is taking interview...");
        result = interviewProcess.process(link);
        System.out.println("Interview result of " + mario.getName() + ": " + (result ? "pass" : "fail"));
    }
}

Output:

Interviewee Mario, majors in JAVA is taking interview...
Mario is on java interview.
Mario failed on java interview!
Interview result of Mario: fail
==================================
Interviewee Link, majors in PYTHON is taking interview...
Java interviewer will not do interview on Link
Link is on python interview.
Link passed python interview!
Link is on HR interview.
Link passed hr interview!
Interview result of Mario: pass

The company's interviewers can register themselves in the interview process. When there are candidates to participate in the interview, the interviewers can choose whether to evaluate the candidates according to whether their majors match. In this process, when there are changes (additions, deletions and modifications) to the Interviewer, we only need to make corresponding changes in the InterviewProcess, without affecting other modules, and separate the changes from the invariants.

The responsibility chain model is implemented in many well-known frameworks:

  1. In Java Servlet FilterChain and Filter , processing the requests one by one.
  2. In Netty ChannelPipeline and ChannelHandler , handling events in the EventLoop.
  3. Plugin s in MyBatis do some chained operations during SQL execution. ... although the specific implementation may not be exactly like the example in the article, the core idea is the same: encapsulate the handler into a handler chain, and then pass the requests along the chain one by one.

4 Summary

When the request needs to be processed by more than one processor, the responsibility chain mode can be considered to decouple the requester and the processor. Connect the head and tail of the processor to form a chain. The objects in the chain do not need to know the structure of the chain, and the request objects only need to be processed by themselves.

The github address of the example in this paper

Keywords: Programming Java Python Netty Mybatis

Added by Nymphetamine on Sat, 06 Jun 2020 13:17:26 +0300