This paper will be introduced from a simple scene, step by step optimization, and finally give specific responsibility chain design mode implementation.

The scene is introduced into

  • First of all, let’s consider a scenario: users want to post on the forum, but their thoughts are varied. They may post normally, they may insert SHALLOW HTML code in the web page, they may use the wrong emoticon format, and they may send some sensitive information.
  • As the administrator of the forum, the user’s posts must be filtered to display, otherwise the forum will not be able to operate. Now let’s consider the simplest way to do this.
public class Demo1 {
    public static void main(String[] args) {
        String msg = "Hi :),  I have to say something super sensitive.";// Suppose there is a post like this
        MsgProcessor mp = new MsgProcessor();
        mp.setMsg(msg);// Process the postSystem.out.println(mp.process()); }}// Post handler
class MsgProcessor{
    private String msg;

    public String process(a){
        // Handle the HTML tag <>
        String str = msg.replace("<"."[").replace(">"."]");
        // Take care of sensitive characters
        str = str.replace("Sensitive"."Normal");
        // Handle the wrong emoticons
        str = str.replace(":)"."^_^");
        return str;
    }
    //get()/set()...
}
// Output the resultHello ^_^, [script]haha[/script] I'm going to say something super normalCopy the code

 

The responsibility chain model is reflected at the beginning

  • As you can see from the code above, the post processor filters posts differently. We can map one filtering method to a filter and extract the filter interface up.
public class Demo2 {
    public static void main(String[] args) {
        String msg = "Hi :),  I have to say something super sensitive.";
        MsgProcessor mp = newMsgProcessor(); mp.setMsg(msg); System.out.println(mp.process()); }}class MsgProcessor{
    private String msg;
    private Filter[] filters = {new HtmlFilter(), new SensitiveFilter(), new ExpressionFilter()};
    public String process(a){
        for(Filter f : filters){
            msg = f.doFilter(msg);
        }
        return msg;
    }

    public String getMsg(a) {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg; }}// Filter interface
interface Filter{
    public String doFilter(String s);
}
// Process HTML tags
class HtmlFilter implements Filter{
    @Override
    public String doFilter(String s) {
        return s.replace("<"."[").replace(">"."]"); }}// Handle sensitive phrases
class SensitiveFilter implements Filter{
    @Override
    public String doFilter(String s) {
        return s.replace("Sensitive"."Normal"); }}// Handle expressions
class ExpressionFilter implements Filter{
    @Override
    public String doFilter(String s) {
        return s.replace(":)"."^_^"); }}Copy the code

 

  • The code above already has a chain of responsibility model. As the post is sent to the server, it passes through three filters in turn, which form a filter chain.

  • Let’s consider what happens if we want to add a new filter chain to our post process, at the end or in the middle of the original chain.
  • The message is processed as it passes through the filter chain. We can treat the filter chain as a filter and let it be implemented as wellFilterInterface, then you can arbitrarily add other filters and chains to a filter chain.
  • The following code implements the filter chainFilterChainAnd replace the original with a filter chainMsgProcessor.
public class Demo3 {
    public static void main(String[] args) {
        String msg = "Hi :),  I have to say something super sensitive.";// Unprocessed posts
        FilterChain fc1 = new FilterChain();// Create a filter chain
        fc1.add(new HtmlFilter())
                .add(new SensitiveFilter());// Add a filter to filter chain 1

        FilterChain fc2 = new FilterChain();// Create a filter chain
        fc2.add(new ExpressionFilter());// Add a filter to filter chain 2

        fc1.add(fc2);// Add chain 2 as a Filter to chain 1 (Filter chain implements Filter interface)

        msg = fc1.doFilter(msg);// Use filter chain 1 to filter postsSystem.out.println(msg); }}class FilterChain implements Filter{

    private List<Filter> list = new ArrayList<>();

    public FilterChain add(Filter filter){
        this.list.add(filter);
        return this;
    }

    @Override
    public String doFilter(String s) {
        for(Filter f : list){
            s = f.doFilter(s);
        }
        returns; }}class HtmlFilter implements Filter{
    @Override
    public String doFilter(String s) {
        return s.replace("<"."[").replace(">"."]"); }}class SensitiveFilter implements Filter{
    @Override
    public String doFilter(String s) {
        return s.replace("Sensitive"."Normal"); }}class ExpressionFilter implements Filter{
    @Override
    public String doFilter(String s) {
        return s.replace(":)"."^_^"); }}interface Filter{
    public String doFilter(String s);
}
Copy the code

 

More sophisticated design, showing the chain of responsibility pattern

  • Before moving on to optimization, let’s consider a more realistic requirement, where a request (a post) is sent as a datagram to the server, which in addition to filtering the request needs to give a response and possibly process the response as well. As shown in the figure below

  • When a message (containing the request body and the response body) is sent to the server, it passes through filters 1, 2, 3. When the processing is complete and the response sending server is wrapped, it also passes through filters 3, 2, 1.
  • You might think it’s a bit like a stack structure, but how does this logic work?
  • First we can have the filter hold a reference to the filter chain, executing each filter in turn by calling the filter chain. In order for filters to execute each filter in turn, the filter holds oneindexSerial number, by which the sequence of execution is controlled. As for the backresponseThe reverse order request is implemented through the method return. This part of the design is difficult to explain clearly with words, please be sure to see the following code and the analysis after the code, with pictures.
  • This part is the essence of the chain of responsibility, understand this part of the code, look at the Web development filter source no pressure.
public class Demo4 {
    public static void main(String[] args) {
        String msg = "Hi :),  I have to say something super sensitive.";// The following three lines simulate a request
        Request request = new Request();
        request.setRequestStr(msg);

        Response response = new Response();/ / response

        FilterChain fc = new FilterChain();// Filter chain
        HtmlFilter f1 = new HtmlFilter();// Create a filter
        SensitiveFilter f2 = new SensitiveFilter();
        ExpressionFilter f3 = new ExpressionFilter();
        fc.add(f1);// Add the filter to the filter chain
        fc.add(f2);
        fc.add(f3);

        fc.doFilter(request, response, fc);// Call the doFilter() method of the filter chain directlySystem.out.println(request.getRequestStr()); }}interface Filter{
    public void doFilter(Request request, Response response, FilterChain fc);
}

class FilterChain implements Filter{

    private List<Filter> list = new ArrayList<>();

    private int index = 0;

    public FilterChain add(Filter filter){
        this.list.add(filter);
        return this;
    }

    @Override
    public void doFilter(Request request, Response response, FilterChain fc) {
        if(index == list.size()){
            return;// If index is the size of the container, the processing of the request is complete.
        }
        Filter f = list.get(index);// Get filter in index orderindex++; f.doFilter(request, response, fc); }}class HtmlFilter implements Filter{
    @Override
    public void doFilter(Request request, Response response, FilterChain fc) {
        request.setRequestStr(request.getRequestStr().replace("<"."[").replace(">"."]"));
        System.out.println("Processing request in HtmlFilter");// Process request first
        fc.doFilter(request, response, fc);// Call the doFilter method of the Filter chain and tell it to execute the doFilter method of the next Filter. The code handling response will be suspended
        System.out.println("Processing response in HtmlFilter"); }}class SensitiveFilter implements Filter{
    @Override
    public void doFilter(Request request, Response response, FilterChain fc) {
        request.setRequestStr(request.getRequestStr().replace("Sensitive"."Normal"));
        System.out.println("Process request in SensitiveFilter");
        fc.doFilter(request, response, fc);
        System.out.println("Handle response in SensitiveFilter"); }}class ExpressionFilter implements Filter{
    @Override
    public void doFilter(Request request, Response response, FilterChain fc) {
        request.setRequestStr(request.getRequestStr().replace(":)"."^_^"));
        System.out.println("Processing request in ExpressionFilter");
        fc.doFilter(request, response, fc);
        System.out.println("Processing response in ExpressionFilter"); }}class Request{
    private String requestStr;// A real Request object contains a lot of information

    public String getRequestStr(a) {
        return requestStr;
    }

    public void setRequestStr(String requestStr) {
        this.requestStr = requestStr; }}class Response{
    private String responseStr;

    public String getResponseStr(a) {
        return responseStr;
    }

    public void setResponseStr(String responseStr) {
        this.responseStr = responseStr; }}Copy the code

 

  • I’ll describe the process once, and you can follow the text to find the corresponding code to understand.
  • So let’s first create one of eachRequestandResponseObject.RequestIt needs to be processed by filters 1, 2, and 3 as it enters the back end,ResponseThe output object is processed by filters 3, 2, and 1 in sequence.
  • Once the request and response objects are created, we create a chain of filters and add filters 1, 2, and 3 in sequence. The entire process will be left to the filter chain.
  • Then we call the filter chaindoFilter()Method to process the Request object
  • This is in the filter chainindexThe value is 0 and passesindexWe find the first filter and call itsdoFilter()Method, let’s look at this code
class HtmlFilter implements Filter{
    @Override
    public void doFilter(Request request, Response response, FilterChain fc) {
        request.setRequestStr(request.getRequestStr().replace("<"."[").replace(">"."]"));
        System.out.println("Processing request in HtmlFilter");// Process request first
        
        fc.doFilter(request, response, fc);// Call the doFilter method of the Filter chain and tell it to execute the doFilter method of the next Filter. The code handling response will be suspended
        
        // Execute response on return
        System.out.println("Processing response in HtmlFilter"); }}Copy the code
  • Enter thedoFilter()After the method, the first will be rightrequestThe request is processed, and then the filter chain is calleddoFilter()Method. That’s the subtlety of the whole chain of responsibility model, it explains whydoFilter()A filter chain argument is added so that each filter can call the filter chain itself to execute the next filter.
  • Why call the filter chain itself? Because when the filter itself is called, the program jumps back to the filter chaindoFilterMethod is executedindexEquals 1, which means you get the second filter, and then you continue.
  • It is because of this jump that the filter pairsresponseThe handler cannot execute temporarily, and must wait for the method above to return to the filter chain before it can be executed.
  • So we’ll see at the endresponseThe response is executed by filters 3, 2, 1(and in reverse order of request).

  • If you still don’t understand the above picture, please leave a message to me.
  • The whole chain of responsibility model has emerged from nothing

 

Read the Tomcat Filter source code for a deeper understanding.

  • I believe that through the above explanation, you have a further understanding of the whole chain of responsibility model.
  • Let’s read the Tomcat Filter source code to feel.
public interface Filter {
    void init(FilterConfig var1) throws ServletException;
    // doFilter(), request, reponse, filterChain.
    void doFilter(ServletRequest var1, ServletResponse var2, FilterChain var3) throws IOException, ServletException;

    void destroy(a);
}
Copy the code
  • We can see thatFilterThe definition of the interface is pretty much the same as we explained, with the addition of initialization and destruction methods.
public interface FilterChain {
    void doFilter(ServletRequest var1, ServletResponse var2) throws IOException, ServletException;
}
Copy the code
  • At the same time we see itFilterChainIt also extracts up into interfaces, but hereFilterChainDo not implementFilterInterface, which means we can’t put twoFilterChainOn the other hand, the filters in Tomcat are not as scalable as in our example^_^

 

  • Further reading
  • Java to achieve a simple network of tanks versus small games
  • The Observer model and the OCP open-closed principle
  • Use of regular expressions in Java