[Design Pattern] Chain of Responsibility Pattern: What if the third-party SMS platform hangs up?

Any fool can write code that a computer can understand. Good programmers write code that people can understand" - Martin Fowler

The pot comes from the sky

The wind is sunny and the sun is shining, I am still writing bug s in my seat as usual, listening to the ditty, very comfortable, I still think in my heart, I haven’t written a blog for a long time, and I don’t have any material recently. Just when I was worried, the material Automatically delivered to your door, and you can write a blog again.

The test brother mentioned a bug to me in Zen Tao: the SMS verification was successful, but the mobile phone never received the SMS verification code, and it failed to retry many times. I rely on, can this work? I was so scared that I hurriedly went to the server to get the logs. After checking the logs, I found that: it turned out that there was a problem with the SMS service provider. We could not connect to the SMS service provider, so the SMS could not be sent out, and then I directly blamed the bug as an external cause. Click to solve.

It's a pity, after the product learned the news, he secretly applied for two SMS operators, plus the one he was using, there were three in total. The product gave a new requirement: when sending SMS verification code, if If you can't connect to the SMS operator, then just switch to another company and continue to send until the text message is sent.

Hey, I have to work overtime to solve this requirement. There are three SMS operators in total. When sending SMS verification codes, first find the first operator. If the first operator fails to send, then find the second operator to send. , if the second operator sends successfully, stop the operation, if the second operator fails to send, continue to find the third operator to send, if all three operators fail to send, then send an email to tell the operator.

This is roughly what you need, if it's you? How would you handle this need? You can think about it first, and then refer to my solution below for a comparison.

Without further ado, let's get started!

When the book is used up, I hate it less. At this time, I know the benefits of reading more. The following is my showtime!

What is the Chain of Responsibility pattern?
The Chain of Responsibility pattern is a design pattern. In the Chain of Responsibility pattern, many objects are connected to form a chain by each object's reference to its subordinate. Requests are passed along this chain until an object on the chain decides to handle the request. The client making the request does not know which object on the chain ultimately handles the request, which allows the system to dynamically reorganize and assign responsibilities without affecting the client. (Baidu Encyclopedia)

In simple terms, there are many processors in the chain of responsibility mode. These processors process a certain request in turn. After processor A is processed, it is handed over to processor B, and processor B is processed and handed over to processor C until there is a processor. until the request can be processed. In this way, can this demand of our SMS be achieved through the chain of responsibility model? The answer is yes.

Let's take a look at how to use the chain of responsibility pattern

Linked list implements the chain of responsibility pattern

package com.liuxing.handler;

/**
 * @ProjectName: handler-of-responsibility-pattern
 * @Package: com.liuxing.handler
 * @ClassName: Handler
 * @Author: Meteor 007
 * @Description: Handler abstract parent class
 * csdn: https://blog.csdn.net/qq_33220089
 * Today's headline: https://www.toutiao.com/c/user/5372182357/#mid=1637641735275523
 * @Version: 1.0
 */
public abstract class Handler {
    /**
     * next processor
     */
    protected Handler nextHandler = null;

    /**
     * set next processor
     * @param nextHandler
     */
    public void setNextHandler(Handler nextHandler) {
        this.nextHandler = nextHandler;
    }

    /**
     * The handler handles the request
     */
    public abstract void handler();
}

package com.liuxing.handler;
/**
 * @ProjectName: handler-of-responsibility-pattern
 * @Package: com.liuxing.handler
 * @ClassName: Handler1
 * @Author: Meteor 007
 * @Description: processor 1
 * csdn: https://blog.csdn.net/qq_33220089
 * Today's headline: https://www.toutiao.com/c/user/5372182357/#mid=1637641735275523
 * @Version: 1.0
 */
public class Handler1 extends Handler {
    @Override
    public void handler() {
        System.out.println("This is the first one handler,Unable to resolve this issue, request the next processor to handle");
        boolean flag = false;
        if(flag){
            return ;
        }
        if(!flag && nextHandler != null){
            nextHandler.handler();
        }
    }
}

package com.liuxing.handler;

/**
 * @ProjectName: handler-of-responsibility-pattern
 * @Package: com.liuxing.handler
 * @ClassName: Handler2
 * @Author: Meteor 007
 * @Description: processor 2
 * csdn: https://blog.csdn.net/qq_33220089
 * Today's headline: https://www.toutiao.com/c/user/5372182357/#mid=1637641735275523
 * @Version: 1.0
 */
public class Handler2 extends Handler {
    @Override
    public void handler() {
        boolean flag = false;
        System.out.println("this is the second handler,Unable to resolve this issue, request the next processor to handle");
        if(flag){
            return ;
        }
        if(!flag && nextHandler != null){
            nextHandler.handler();
        }
    }
}

package com.liuxing.handler;

/**
 * @ProjectName: handler-of-responsibility-pattern
 * @Package: com.liuxing.handler
 * @ClassName: Handler3
 * @Author: Meteor 007
 * @Description: processor 3
 * csdn: https://blog.csdn.net/qq_33220089
 * Today's headline: https://www.toutiao.com/c/user/5372182357/#mid=1637641735275523
 * @Version: 1.0
 */
public class Handler3 extends Handler {
    @Override
    public void handler() {
        boolean flag = true;
        System.out.println("this is the third handler,Problem solved, no need to go down");
        if(flag){
            return ;
        }
        if(!flag && nextHandler != null){
            nextHandler.handler();
        }
        System.out.println("Execution ends, no handler can resolve the request");
    }
}

package com.liuxing.chain;

import com.liuxing.handler.Handler;


/**
 * @ProjectName: handler-of-responsibility-pattern
 * @Package: com.liuxing.chain
 * @ClassName: ChainHandler
 * @Author: Meteor 007
 * @Description: processor chain
 * csdn: https://blog.csdn.net/qq_33220089
 * Today's headline: https://www.toutiao.com/c/user/5372182357/#mid=1637641735275523
 * @Version: 1.0
 */
public class ChainHandler {
    //first processor
    private Handler firstHandler;
    //last processor
    private Handler endHandler;

    /**
     * add processor
     * @param handler
     */
    public void addHandler(Handler handler){
        //Set the next processor of the current processor to null, this step can also be omitted
        handler.setNextHandler(null);
        //Determine if it is the first handler. If it is the first handler, set both firstHandler and endHandler to: handler
        if(firstHandler == null ){
            firstHandler = handler;
            endHandler = handler;
            return ;
        }
        //If not the first processor, add the current processor to the next processor of the previous last processor
        endHandler.setNextHandler(handler);
        //Modify the last handler to the currently added handler
        endHandler = handler;
    }

    /**
     * start executing handler
     */
    public void handler(){
        if(firstHandler == null ){
            return ;
        }
        firstHandler.handler();
    }


}

package com.liuxing.test;

import com.liuxing.chain.ChainHandler;
import com.liuxing.handler.Handler1;
import com.liuxing.handler.Handler2;
import com.liuxing.handler.Handler3;

public class HandlerTest {

    public static void main(String[] args) {
        ChainHandler chainHandler = new ChainHandler();
        chainHandler.addHandler(new Handler1());
        chainHandler.addHandler(new Handler2());
        chainHandler.addHandler(new Handler3());
        chainHandler.handler();
    }
}

This is the demo of the chain of responsibility mode. There is an abstract parent class: Handler (handler abstract parent class); three subclasses: Handler1, Handler2, Handler3; rewriting the handler method in the parent class requires a processor chain: ChainHandler , the processor chain contains two processors at the beginning and the end, a method for adding a processor, and a method for executing the handler. Let's take a look at the execution result of this demo:

This is the first one handler,Unable to resolve this issue, request the next processor to handle
 this is the second handler,Unable to resolve this issue, request the next processor to handle
 this is the third handler,Problem solved, no need to go down

Process finished with exit code 0

We found that when it reached the third processor, it stopped executing.

I believe everyone knows how to use the chain of responsibility model. I also believe that everyone should know how to transform the needs of SMS. I will not write the code transformation of SMS here. If you are perceptive, you can study it yourself. Yes, very simple.

So is this the end of this blog? Of course not, the point is just beginning.

The above chain of responsibility mode makes people look awkward, because it has not been optimized yet. We found that the next processor needs to be called in the three subclasses, right? Is this troublesome? If someone adds a processor What about forgetting to call the next handler? Does the bug come, and then you need to work overtime, so this kind of thing must be killed in the cradle, we do not directly call the next processor in the subclass, we put the call to the next processor in the abstract parent class , let him deal with it uniformly, and it can be solved perfectly.

see code

package com.liuxing.handler;

/**
 * @ProjectName: handler-of-responsibility-pattern
 * @Package: com.liuxing.handler
 * @ClassName: Handler
 * @Author: Meteor 007
 * @Description: Handler abstract parent class
 * csdn: https://blog.csdn.net/qq_33220089
 * Today's headline: https://www.toutiao.com/c/user/5372182357/#mid=1637641735275523
 * @Version: 1.0
 */
public abstract class Handler {
    /**
     * next processor
     */
    protected Handler nextHandler = null;

    /**
     * set next processor
     * @param nextHandler
     */
    public void setNextHandler(Handler nextHandler) {
        this.nextHandler = nextHandler;
    }

    public final void handler(){
        boolean flag = doHandler();
        if(!flag && nextHandler!= null){
            nextHandler.handler();
        }
    }

    /**
     * The handler handles the request
     */
    protected abstract boolean doHandler();
}

package com.liuxing.handler;
/**
 * @ProjectName: handler-of-responsibility-pattern
 * @Package: com.liuxing.handler
 * @ClassName: Handler1
 * @Author: Meteor 007
 * @Description: processor 1
 * csdn: https://blog.csdn.net/qq_33220089
 * Today's headline: https://www.toutiao.com/c/user/5372182357/#mid=1637641735275523
 * @Version: 1.0
 */
public class Handler1 extends Handler {

    @Override
    public boolean doHandler() {
        System.out.println("This is the first one handler,Unable to resolve this issue, request the next processor to handle");
        return false;
    }
}

package com.liuxing.handler;

/**
 * @ProjectName: handler-of-responsibility-pattern
 * @Package: com.liuxing.handler
 * @ClassName: Handler2
 * @Author: Meteor 007
 * @Description: processor 2
 * csdn: https://blog.csdn.net/qq_33220089
 * Today's headline: https://www.toutiao.com/c/user/5372182357/#mid=1637641735275523
 * @Version: 1.0
 */
public class Handler2 extends Handler {
    @Override
    public boolean doHandler() {
        System.out.println("this is the second handler,Unable to resolve this issue, request the next processor to handle");
       return false;
    }
}

package com.liuxing.handler;

/**
 * @ProjectName: handler-of-responsibility-pattern
 * @Package: com.liuxing.handler
 * @ClassName: Handler3
 * @Author: Meteor 007
 * @Description: processor 3
 * csdn: https://blog.csdn.net/qq_33220089
 * Today's headline: https://www.toutiao.com/c/user/5372182357/#mid=1637641735275523
 * @Version: 1.0
 */
public class Handler3 extends Handler {
    @Override
    public boolean doHandler() {
        boolean flag = true;
        System.out.println("this is the third handler,Problem solved, no need to go down");
        if(flag){
            return true;
        }
        System.out.println("Execution ends, no handler can resolve the request");
        return false;
    }
}

The other code remains unchanged. We found that the code that calls the next handler has been moved to the abstract parent class handler Handler. Self-reliance only needs to deal with its own logic with peace of mind. Does it reduce the occurrence rate of bug s?

Here is a little knowledge point, everyone until I am in the abstract parent class: Why set handler(); as final in Handler? If you know, you can uninstall the comment section.

Implementing the Chain of Responsibility pattern using arrays

This implementation method is simpler and easier to understand than the above-mentioned implementation method based on linked list. Without further ado, just go to the code.

package com.liuxing.handler;

/**
 * @ProjectName: demo
 * @Package: com.liuxing.handler
 * @ClassName: IHandler
 * @Author: Meteor 007
 * @Description: processor interface
 * csdn: https://blog.csdn.net/qq_33220089
 * Today's headline: https://www.toutiao.com/c/user/5372182357/#mid=1637641735275523
 * @Date: 2020/7/13 17:17
 * @Version: 1.0
 */
public interface IHandler {

    boolean handler();
}

package com.liuxing.handler;
/**
 * @ProjectName: handler-of-responsibility-pattern
 * @Package: com.liuxing.handler
 * @ClassName: Handler1
 * @Author: Meteor 007
 * @Description: processor 1
 * csdn: https://blog.csdn.net/qq_33220089
 * Today's headline: https://www.toutiao.com/c/user/5372182357/#mid=1637641735275523
 * @Version: 1.0
 */
public class Handler1 implements IHandler {


    @Override
    public boolean handler() {
        System.out.println("This is the first one handler,Unable to resolve this issue, request the next processor to handle");
        return false;
    }
}

package com.liuxing.handler;

/**
 * @ProjectName: handler-of-responsibility-pattern
 * @Package: com.liuxing.handler
 * @ClassName: Handler2
 * @Author: Meteor 007
 * @Description: processor 2
 * csdn: https://blog.csdn.net/qq_33220089
 * Today's headline: https://www.toutiao.com/c/user/5372182357/#mid=1637641735275523
 * @Version: 1.0
 */
public class Handler2 implements IHandler {
    @Override
    public boolean handler() {
        System.out.println("this is the second handler,Unable to resolve this issue, request the next processor to handle");
       return false;
    }
}

package com.liuxing.handler;

/**
 * @ProjectName: handler-of-responsibility-pattern
 * @Package: com.liuxing.handler
 * @ClassName: Handler3
 * @Author: Meteor 007
 * @Description: processor 3
 * csdn: https://blog.csdn.net/qq_33220089
 * Today's headline: https://www.toutiao.com/c/user/5372182357/#mid=1637641735275523
 * @Version: 1.0
 */
public class Handler3 implements IHandler{
    @Override
    public boolean handler() {
        boolean flag = true;
        System.out.println("this is the third handler,Problem solved, no need to go down");
        if(flag){
            return true;
        }
        System.out.println("Execution ends, no handler can resolve the request");
        return false;
    }
}

package com.liuxing.chain;

import com.liuxing.handler.Handler;
import com.liuxing.handler.IHandler;
import org.springframework.util.CollectionUtils;

import java.util.ArrayList;
import java.util.List;


/**
 * @ProjectName: handler-of-responsibility-pattern
 * @Package: com.liuxing.chain
 * @ClassName: ChainHandler
 * @Author: Meteor 007
 * @Description: processor chain
 * csdn: https://blog.csdn.net/qq_33220089
 * Today's headline: https://www.toutiao.com/c/user/5372182357/#mid=1637641735275523
 * @Version: 1.0
 */
public class ChainHandler {
    /**
     * all processors
     */
    private List<IHandler> list = new ArrayList<IHandler>();

    /**
     * add processor
     * @param handler
     */
    public void addHandler(IHandler handler){
        list.add(handler);
    }

    /**
     * start executing handler
     */
    public void handler(){
        if(CollectionUtils.isEmpty(list)){
            return ;
        }
        for(IHandler handler: list){
           if(handler.handler()){
               return;
           }
        }
    }
}

package com.liuxing.test;

import com.liuxing.chain.ChainHandler;
import com.liuxing.handler.Handler1;
import com.liuxing.handler.Handler2;
import com.liuxing.handler.Handler3;

public class HandlerTest {

    public static void main(String[] args) {
        ChainHandler chainHandler = new ChainHandler();
        chainHandler.addHandler(new Handler1());
        chainHandler.addHandler(new Handler2());
        chainHandler.addHandler(new Handler3());
        chainHandler.handler();
    }
}

Well, the code transformation is completed. Compared with the first method, this method is easier to understand, because ArrayList is ordered, so when the loop is executed, it is executed according to the first added first, and you want to control the execution of the processor. Order, you only need to control the order in which the list is added, which is simple and convenient.

Mutation mode of blame company mode

In the process of redevelopment of the chain of responsibility model, a new mutation model is gradually derived. What kind of model? Normal responsibility chain mode: As long as there is a processor that can handle the request, the current process is ended, and the variant version is that regardless of whether the current processor can handle the request, it will be executed in sequence until all processors are executed. This mutation mode is more common in sensitive word filtering in games.

Chain of Responsibility Pattern Application Scenario

1. Filter sensitive words in the game

2. The handler in the servlet also adopts the responsibility chain mode. Interested partners can go and see.

3. More switch-case s can also be optimized using the blame connection mode.

Summarize

What is the blaming even mode?
The Chain of Responsibility pattern is a design pattern. In the Chain of Responsibility pattern, many objects are connected to form a chain by each object's reference to its subordinate. Requests are passed along this chain until an object on the chain decides to handle the request. The client making the request does not know which object on the chain ultimately handles the request, which allows the system to dynamically reorganize and assign responsibilities without affecting the client. (Baidu Encyclopedia)

In simple terms, there are many processors in the chain of responsibility mode. These processors process a certain request in turn. After processor A is processed, it is handed over to processor B, and processor B is processed and handed over to processor C until there is a processor. until the request can be processed.

Chain of Responsibility Mutation Pattern
Compared with the normal chain of responsibility mode, it needs to execute all processors, which is more common in game-sensitive word filtering.

common design patterns

[Design Pattern]Singleton Pattern

[Design Patterns] Factory Pattern: Are you still using a bunch of if/else to create objects?

[Design Patterns] Builder Pattern: Is the way you create objects smooth?

[Design Pattern] Prototype Pattern: How to quickly clone an object?

[Design Pattern] Strategy Pattern: I am a strategic class

[Design Patterns] Observer Pattern: Can a registration function also use design patterns?

[Design Pattern] Facade Pattern: The interface is like a facade, you can see your code level at a glance

Tags: Java Design Pattern

Posted by lisa3711 on Thu, 02 Jun 2022 02:12:20 +0530