Chain of responsibility design patterns

Reference

[1] zhuanlan.zhihu.com/p/99334096, this paper draw lessons from the article, if there are any infringement, delete contact

[2] refactoringguru. Cn/design – patt…

[3] c.biancheng.net/view/1383.h…

What is the chain of responsibility

The chain of responsibility pattern is a behavior design pattern that allows you to send requests down a chain of handlers. Upon receipt of the request, each handler can either process it or pass it on to the next handler on the chain.

scenario

There are many scenarios for using the chain of responsibility

  • Multi-condition process judgment: permission control
  • ERP system process approval: General manager, hr manager, project manager
  • The underlying implementation of Java filters, Filter

Not using this design pattern can make code bloated or difficult to maintain when requirements change, as in the following example

counter-examples

Suppose there is now a level game, the condition to enter the next level is the previous level score is higher than XX

  1. The game consists of three levels
  2. Entering the second level requires a score of 80 or more in the first level
  3. Entering the third level requires a score of 90 or more in the second level

So the code could be written like this

/ / the first level
public class FirstPassHandler {
    public int handler(a){
        System.out.println("First pass -->FirstPassHandler");
        return 80; }}/ / the second level
public class SecondPassHandler {
    public int handler(a){
        System.out.println("Second level -->SecondPassHandler");
        return 90; }}/ / the third level
public class ThirdPassHandler {
    public int handler(a){
        System.out.println("Level 3 -->ThirdPassHandler, this is the last level");
        return 95; }}/ / the client
public class HandlerClient {
    public static void main(String[] args) {

        FirstPassHandler firstPassHandler = new FirstPassHandler();/ / the first level
        SecondPassHandler secondPassHandler = new SecondPassHandler();/ / the second level
        ThirdPassHandler thirdPassHandler = new ThirdPassHandler();/ / the third level

        int firstScore = firstPassHandler.handler();
        // If you score 80 or more in level 1, enter level 2
        if(firstScore >= 80) {int secondScore = secondPassHandler.handler();
            // Enter level 2 if the score is 90 or greater
            if(secondScore >= 90){ thirdPassHandler.handler(); }}}}Copy the code

So if the game had 100 levels, our code would probably look something like this

if(the first1Close by) {// Level 2
    if(the first2Close by) {// Level 3
        if(the first3Close by) {// Level 4
            if(the first4Close by) {// Level 5
                if(the first5Close by) {// Level 6
                    if(the first6Close by) {/ /...
                    }
                }
            } 
        }
    }
}
Copy the code

Not only is this code redundant, but the risk of making a significant change to the code when we need to tweak two levels is high, so it’s pretty bad to write

A preliminary reform

How to solve this problem, we can link up each pass through the linked list, form the way of responsibility chain, the first pass is the second pass, the second pass is the third pass…. , so that the client does not need to make multiple if judgments

public class FirstPassHandler {
    /** * The next level after the first level is the second level */
    private SecondPassHandler secondPassHandler;

    public void setSecondPassHandler(SecondPassHandler secondPassHandler) {
        this.secondPassHandler = secondPassHandler;
    }

    // Score for this level
    private int play(a){
        return 80;
    }

    public int handler(a){
        System.out.println("First pass -->FirstPassHandler");
        if(play() >= 80) {// Score >=80 and the next level exists before entering the next level
            if(this.secondPassHandler ! =null) {return this.secondPassHandler.handler(); }}return 80; }}public class SecondPassHandler {

    /** * The next level to the second level is the third level */
    private ThirdPassHandler thirdPassHandler;

    public void setThirdPassHandler(ThirdPassHandler thirdPassHandler) {
        this.thirdPassHandler = thirdPassHandler;
    }

    // Score for this level
    private int play(a){
        return 90;
    }

    public int handler(a){
        System.out.println("Second level -->SecondPassHandler");

        if(play() >= 90) {// Score >=90 and the next level exists before entering the next level
            if(this.thirdPassHandler ! =null) {return this.thirdPassHandler.handler(); }}return 90; }}public class ThirdPassHandler {

    // Score for this level
    private int play(a){
        return 95;
    }

    /** ** This is the last level, so there is no next level */
    public int handler(a){
        System.out.println("Level 3 -->ThirdPassHandler, this is the last level");
        returnplay(); }}public class HandlerClient {
    public static void main(String[] args) {

        FirstPassHandler firstPassHandler = new FirstPassHandler();/ / the first level
        SecondPassHandler secondPassHandler = new SecondPassHandler();/ / the second level
        ThirdPassHandler thirdPassHandler = new ThirdPassHandler();/ / the third level

        firstPassHandler.setSecondPassHandler(secondPassHandler);// The next level after the first level is the second level
        secondPassHandler.setThirdPassHandler(thirdPassHandler);// The next level of the second level is the third level

        // Note: Since the third level is the last level, there is no next level
        // Start calling the first level and decide whether to proceed to the next level in each levelfirstPassHandler.handler(); }}Copy the code

disadvantages

Shortcomings of existing models

  • Each level has a member variable for the next level and is different, making it very inconvenient to form chains
  • The code doesn’t scale very well

Transformation of responsibility chain

  • Since each level has a member variable for the next level and is different, we can abstract out a parent class or interface on the level that each concrete level inherits or implements

With the idea, let’s first briefly introduce the basic composition of the responsibility chain design mode

  • The role of Abstract Handler: Defines an interface to process a request, including abstract processing methods and a subsequent connection.
  • The role of Concrete Handler: Implements the processing method of the abstract Handler, determines whether the request can be processed, if it can be processed, and otherwise passes the request to its successor.
  • The Client role: Creates a processing chain and submits a request to a specific handler object in the header. It does not care about processing details or the delivery of the request.

public abstract class AbstractHandler {

    /** * the next level uses the current abstract class to receive */
    protected AbstractHandler next;

    public void setNext(AbstractHandler next) {
        this.next = next;
    }

    public abstract int handler(a);
}

public class FirstPassHandler extends AbstractHandler{

    private int play(a){
        return 80;
    }

    @Override
    public int handler(a){
        System.out.println("First pass -->FirstPassHandler");
        int score = play();
        if(score >= 80) {// Score >=80 and the next level exists before entering the next level
            if(this.next ! =null) {return this.next.handler(); }}returnscore; }}public class SecondPassHandler extends AbstractHandler{

    private int play(a){
        return 90;
    }

    public int handler(a){
        System.out.println("Second level -->SecondPassHandler");

        int score = play();
        if(score >= 90) {// Score >=90 and the next level exists before entering the next level
            if(this.next ! =null) {return this.next.handler(); }}returnscore; }}public class ThirdPassHandler extends AbstractHandler{

    private int play(a){
        return 95;
    }

    public int handler(a){
        System.out.println("Level 3 -->ThirdPassHandler);
        int score = play();
        if(score >= 95) {// Score >=95 and the next level exists before entering the next level
            if(this.next ! =null) {return this.next.handler(); }}returnscore; }}public class HandlerClient {
    public static void main(String[] args) {

        FirstPassHandler firstPassHandler = new FirstPassHandler();/ / the first level
        SecondPassHandler secondPassHandler = new SecondPassHandler();/ / the second level
        ThirdPassHandler thirdPassHandler = new ThirdPassHandler();/ / the third level

        // Only the set method here has changed compared to the unchanged client code above, everything else is the same
        firstPassHandler.setNext(secondPassHandler);// The next level after the first level is the second level
        secondPassHandler.setNext(thirdPassHandler);// The next level of the second level is the third level

        // Note: Since the third level is the last level, there is no next level

        // Start with the first levelfirstPassHandler.handler(); }}Copy the code

Responsibility chain factory transformation

For the above request chain, we can also maintain this relationship in a configuration file or an enumeration. I’m going to use enumeration to teach you how to dynamically configure the request chain and form a call chain for each requester, okay

public enum GatewayEnum {
    // handlerId, interceptor name, fully qualified class name, preHandlerId, nextHandlerId
    API_HANDLER(new GatewayEntity(1."API interface flow limiting"."cn.dgut.design.chain_of_responsibility.GateWay.impl.ApiLimitGatewayHandler".null.2)),
    BLACKLIST_HANDLER(new GatewayEntity(2."Blacklist blocking"."cn.dgut.design.chain_of_responsibility.GateWay.impl.BlacklistGatewayHandler".1.3)),
    SESSION_HANDLER(new GatewayEntity(3."User session interception"."cn.dgut.design.chain_of_responsibility.GateWay.impl.SessionGatewayHandler".2.null)); GatewayEntity gatewayEntity;public GatewayEntity getGatewayEntity(a) {
        return gatewayEntity;
    }

    GatewayEnum(GatewayEntity gatewayEntity) {
        this.gatewayEntity = gatewayEntity; }}public class GatewayEntity {

    private String name;

    private String conference;

    private Integer handlerId;

    private Integer preHandlerId;

    private Integer nextHandlerId;
}


public interface GatewayDao {

    /** * Get configuration item * according to handlerId@param handlerId
     * @return* /
    GatewayEntity getGatewayEntity(Integer handlerId);

    /** * gets the first handler *@return* /
    GatewayEntity getFirstGatewayEntity(a);
}

public class GatewayImpl implements GatewayDao {

    /** * initializes the handler configured in enumeration to map to obtain */
    private static Map<Integer, GatewayEntity> gatewayEntityMap = new HashMap<>();

    static {
        GatewayEnum[] values = GatewayEnum.values();
        for(GatewayEnum value : values) { GatewayEntity gatewayEntity = value.getGatewayEntity(); gatewayEntityMap.put(gatewayEntity.getHandlerId(), gatewayEntity); }}@Override
    public GatewayEntity getGatewayEntity(Integer handlerId) {
        return gatewayEntityMap.get(handlerId);
    }

    @Override
    public GatewayEntity getFirstGatewayEntity(a) {
        for (Map.Entry<Integer, GatewayEntity> entry : gatewayEntityMap.entrySet()) {
            GatewayEntity value = entry.getValue();
            // No previous handler is the first handler
            if (value.getPreHandlerId() == null) {
                returnvalue; }}return null; }}public class GatewayHandlerEnumFactory {

    private static GatewayDao gatewayDao = new GatewayImpl();

    // Provide static methods to get the first handler
    public static GatewayHandler getFirstGatewayHandler(a) {

        GatewayEntity firstGatewayEntity = gatewayDao.getFirstGatewayEntity();
        GatewayHandler firstGatewayHandler = newGatewayHandler(firstGatewayEntity);
        if (firstGatewayHandler == null) {
            return null;
        }

        GatewayEntity tempGatewayEntity = firstGatewayEntity;
        Integer nextHandlerId = null;
        GatewayHandler tempGatewayHandler = firstGatewayHandler;
        // Iterate through all the handlers and link them together
        while((nextHandlerId = tempGatewayEntity.getNextHandlerId()) ! =null) {
            GatewayEntity gatewayEntity = gatewayDao.getGatewayEntity(nextHandlerId);
            GatewayHandler gatewayHandler = newGatewayHandler(gatewayEntity);
            tempGatewayHandler.setNext(gatewayHandler);
            tempGatewayHandler = gatewayHandler;
            tempGatewayEntity = gatewayEntity;
        }
	// Return the first handler
        return firstGatewayHandler;
    }

    /** * Reflection materializes the concrete handler@param firstGatewayEntity
     * @return* /
    private static GatewayHandler newGatewayHandler(GatewayEntity firstGatewayEntity) {
        // Get the fully qualified class name
        String className = firstGatewayEntity.getConference(); 
        try {
            // Load and initialize the class according to the fully qualified class name, that is, initialize the static segment of the classClass<? > clazz = Class.forName(className);return (GatewayHandler) clazz.newInstance();
        } catch (ClassNotFoundException | IllegalAccessException | InstantiationException e) {
            e.printStackTrace();
        }
        return null; }}public class GetewayClient {
    public static void main(String[] args) { GetewayHandler firstGetewayHandler = GetewayHandlerEnumFactory.getFirstGetewayHandler(); firstGetewayHandler.service(); }}Copy the code

Talk to other

I haven’t written an article for a long time. This article mainly refers to the author’s pictures and texts in Reference [1]. To be honest, it is very good, especially the responsibility chain factory, which I find very interesting.

Design patterns are indeed an art, and still need work!

In order to read

If you want to learn more about the chain of responsibility model, click through the following article.

  • Hot Swap Permission Control using Responsibility Chain Mode
  • Combining the Chain of Responsibility model with the Builder Model
  • Application of the Chain of Responsibility model in JDK source Code