laitimes

Yesterday, Senior Ali wrote a chain of responsibility model, and there were countless bugs

author:Program Ape Mouth

directory

  • background
  • What is the chain of responsibility
  • Usage scenarios
  • epilogue

background

Recently, I asked a member of my team to write an import feature. He used the chain of responsibility pattern, the code was piled up very much, and there were many bugs, which did not achieve the effect I expected.

In fact, for the import function, I think the template method is more appropriate! To this end, the team next door also came up with our case and conducted a collective code review.

Learn the design pattern well, and don't force it for the sake of practice! Let the function that could be achieved in 100 lines write 3000 lines! Leaving this aside, let's first look at the design pattern of the chain of responsibility!

What is the chain of responsibility

The Chain of Responsibility pattern is a behavioral design pattern that allows you to send requests along a processor link. After receiving the request, each processor can process the request or pass it to the next processor on the chain.

Yesterday, Senior Ali wrote a chain of responsibility model, and there were countless bugs

Image

Usage scenarios

There are still many use cases of the chain of responsibility:

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

If you don't use this design pattern, it can make the code bloated or difficult to maintain when the requirements change, as in the example below.

| counterexample

Suppose you have a game that breaks through now, and the condition for progressing to the next level is that the score of the previous level is higher than xx:

  • The game has a total of 3 levels
  • To enter the second level, the game score of the first level is greater than or equal to 80
  • To enter the third level, the game score of the second level is greater than or equal to 90

Then the code can be written like this:

//第一关  
public class FirstPassHandler {  
    public int handler(){  
        System.out.println("第一关-->FirstPassHandler");  
        return 80;  
    }  
}  
  
//第二关  
public class SecondPassHandler {  
    public int handler(){  
        System.out.println("第二关-->SecondPassHandler");  
        return 90;  
    }  
}  
  
  
//第三关  
public class ThirdPassHandler {  
    public int handler(){  
        System.out.println("第三关-->ThirdPassHandler,这是最后一关啦");  
        return 95;  
    }  
}  
  
  
//客户端  
public class HandlerClient {  
    public static void main(String[] args) {  
  
        FirstPassHandler firstPassHandler = new FirstPassHandler();//第一关  
        SecondPassHandler secondPassHandler = new SecondPassHandler();//第二关  
        ThirdPassHandler thirdPassHandler = new ThirdPassHandler();//第三关  
  
        int firstScore = firstPassHandler.handler();  
        //第一关的分数大于等于80则进入第二关  
        if(firstScore >= 80){  
            int secondScore = secondPassHandler.handler();  
            //第二关的分数大于等于90则进入第二关  
            if(secondScore >= 90){  
                thirdPassHandler.handler();  
            }  
        }  
    }  
}  
           

So if the game had 100 levels, our code would most likely have been written like this:

if(第1关通过){  
    // 第2关 游戏  
    if(第2关通过){  
        // 第3关 游戏  
        if(第3关通过){  
           // 第4关 游戏  
            if(第4关通过){  
                // 第5关 游戏  
                if(第5关通过){  
                    // 第6关 游戏  
                    if(第6关通过){  
                        //...  
                    }  
                }  
            }   
        }  
    }  
}  
           

Not only is this code redundant, but it also makes very big changes to the code when we want to make adjustments to two levels, which is a very risky operation, so it is written very badly.

| Initial retrofit

How to solve this problem, we can connect each level through the linked list, forming a chain of responsibility in a way, the first level is passed after the second level, the second level is passed after the third level....

This way the client does not need to make multiple if judgments:

public class FirstPassHandler {  
    /**  
     * 第一关的下一关是 第二关  
     */  
    private SecondPassHandler secondPassHandler;  
  
    public void setSecondPassHandler(SecondPassHandler secondPassHandler) {  
        this.secondPassHandler = secondPassHandler;  
    }  
  
    //本关卡游戏得分  
    private int play(){  
        return 80;  
    }  
  
    public int handler(){  
        System.out.println("第一关-->FirstPassHandler");  
        if(play() >= 80){  
            //分数>=80 并且存在下一关才进入下一关  
            if(this.secondPassHandler != null){  
                return this.secondPassHandler.handler();  
            }  
        }  
  
        return 80;  
    }  
}  
  
public class SecondPassHandler {  
  
    /**  
     * 第二关的下一关是 第三关  
     */  
    private ThirdPassHandler thirdPassHandler;  
  
    public void setThirdPassHandler(ThirdPassHandler thirdPassHandler) {  
        this.thirdPassHandler = thirdPassHandler;  
    }  
  
    //本关卡游戏得分  
    private int play(){  
        return 90;  
    }  
  
    public int handler(){  
        System.out.println("第二关-->SecondPassHandler");  
  
        if(play() >= 90){  
            //分数>=90 并且存在下一关才进入下一关  
            if(this.thirdPassHandler != null){  
                return this.thirdPassHandler.handler();  
            }  
        }  
  
        return 90;  
    }  
}  
  
public class ThirdPassHandler {  
  
    //本关卡游戏得分  
    private int play(){  
        return 95;  
    }  
  
    /**  
     * 这是最后一关,因此没有下一关  
     */  
    public int handler(){  
        System.out.println("第三关-->ThirdPassHandler,这是最后一关啦");  
        return play();  
    }  
}  
  
public class HandlerClient {  
    public static void main(String[] args) {  
  
        FirstPassHandler firstPassHandler = new FirstPassHandler();//第一关  
        SecondPassHandler secondPassHandler = new SecondPassHandler();//第二关  
        ThirdPassHandler thirdPassHandler = new ThirdPassHandler();//第三关  
  
        firstPassHandler.setSecondPassHandler(secondPassHandler);//第一关的下一关是第二关  
        secondPassHandler.setThirdPassHandler(thirdPassHandler);//第二关的下一关是第三关  
  
        //说明:因为第三关是最后一关,因此没有下一关  
        //开始调用第一关 每一个关卡是否进入下一关卡 在每个关卡中判断  
        firstPassHandler.handler();  
  
    }  
}  
           

| shortcoming

Disadvantages of the existing model:

  • Each level has a member variable for the next level and is different, making it inconvenient to form a chain
  • The code is very poorly extensibility

| Chain of responsibility retrofitting

Since each level has member variables for the next level and is different, we can abstract a parent class or interface on the level, and then inherit or implement each specific level.

With this in mind, let's briefly introduce the basic components of the chain of responsibility design model:

  • Handler role: Defines an interface for handling requests, containing abstract processing methods and a successor connection.
  • Concrete Handler role: Implements the abstract processor's processing method to determine whether the request can be processed, and if it can be processed, then processed, otherwise the request is forwarded to its successor.
  • Client role: Creates a processing chain and submits a request to the specific processor object of the chain header, which does not care about the processing details and the process of passing the request.
Yesterday, Senior Ali wrote a chain of responsibility model, and there were countless bugs

Image

public abstract class AbstractHandler {  
  
    /**  
     * 下一关用当前抽象类来接收  
     */  
    protected AbstractHandler next;  
  
    public void setNext(AbstractHandler next) {  
        this.next = next;  
    }  
  
    public abstract int handler();  
}  
  
public class FirstPassHandler extends AbstractHandler{  
  
    private int play(){  
        return 80;  
    }  
  
    @Override  
    public int handler(){  
        System.out.println("第一关-->FirstPassHandler");  
        int score = play();  
        if(score >= 80){  
            //分数>=80 并且存在下一关才进入下一关  
            if(this.next != null){  
                return this.next.handler();  
            }  
        }  
        return score;  
    }  
}  
  
public class SecondPassHandler extends AbstractHandler{  
  
    private int play(){  
        return 90;  
    }  
  
    public int handler(){  
        System.out.println("第二关-->SecondPassHandler");  
  
        int score = play();  
        if(score >= 90){  
            //分数>=90 并且存在下一关才进入下一关  
            if(this.next != null){  
                return this.next.handler();  
            }  
        }  
  
        return score;  
    }  
}  
  
public class ThirdPassHandler extends AbstractHandler{  
  
    private int play(){  
        return 95;  
    }  
  
    public int handler(){  
        System.out.println("第三关-->ThirdPassHandler");  
        int score = play();  
        if(score >= 95){  
            //分数>=95 并且存在下一关才进入下一关  
            if(this.next != null){  
                return this.next.handler();  
            }  
        }  
        return score;  
    }  
}  
  
public class HandlerClient {  
    public static void main(String[] args) {  
  
        FirstPassHandler firstPassHandler = new FirstPassHandler();//第一关  
        SecondPassHandler secondPassHandler = new SecondPassHandler();//第二关  
        ThirdPassHandler thirdPassHandler = new ThirdPassHandler();//第三关  
  
        // 和上面没有更改的客户端代码相比,只有这里的set方法发生变化,其他都是一样的  
        firstPassHandler.setNext(secondPassHandler);//第一关的下一关是第二关  
        secondPassHandler.setNext(thirdPassHandler);//第二关的下一关是第三关  
  
        //说明:因为第三关是最后一关,因此没有下一关  
  
        //从第一个关卡开始  
        firstPassHandler.handler();  
  
    }  
}  
           

| Chain of Responsibility Factory Transformation

For the request chain above, we can also maintain this relationship into a configuration file or an enumeration. I'll use enumerations to teach you how to dynamically configure the request chain and form a chain of calls for each requestor.

Yesterday, Senior Ali wrote a chain of responsibility model, and there were countless bugs

Image

public enum GatewayEnum {  
    // handlerId, 拦截者名称,全限定类名,preHandlerId,nextHandlerId  
    API_HANDLER(new GatewayEntity(1, "api接口限流", "cn.dgut.design.chain_of_responsibility.GateWay.impl.ApiLimitGatewayHandler", null, 2)),  
    BLACKLIST_HANDLER(new GatewayEntity(2, "黑名单拦截", "cn.dgut.design.chain_of_responsibility.GateWay.impl.BlacklistGatewayHandler", 1, 3)),  
    SESSION_HANDLER(new GatewayEntity(3, "用户会话拦截", "cn.dgut.design.chain_of_responsibility.GateWay.impl.SessionGatewayHandler", 2, null)),  
    ;  
  
    GatewayEntity gatewayEntity;  
  
    public GatewayEntity getGatewayEntity() {  
        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 {  
  
    /**  
     * 根据 handlerId 获取配置项  
     * @param handlerId  
     * @return  
     */  
    GatewayEntity getGatewayEntity(Integer handlerId);  
  
    /**  
     * 获取第一个处理者  
     * @return  
     */  
    GatewayEntity getFirstGatewayEntity();  
}  
  
public class GatewayImpl implements GatewayDao {  
  
    /**  
     * 初始化,将枚举中配置的handler初始化到map中,方便获取  
     */  
    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() {  
        for (Map.Entry<Integer, GatewayEntity> entry : gatewayEntityMap.entrySet()) {  
            GatewayEntity value = entry.getValue();  
            //  没有上一个handler的就是第一个  
            if (value.getPreHandlerId() == null) {  
                return value;  
            }  
        }  
        return null;  
    }  
}  
  
public class GatewayHandlerEnumFactory {  
  
    private static GatewayDao gatewayDao = new GatewayImpl();  
  
    // 提供静态方法,获取第一个handler  
    public static GatewayHandler getFirstGatewayHandler() {  
  
        GatewayEntity firstGatewayEntity = gatewayDao.getFirstGatewayEntity();  
        GatewayHandler firstGatewayHandler = newGatewayHandler(firstGatewayEntity);  
        if (firstGatewayHandler == null) {  
            return null;  
        }  
  
        GatewayEntity tempGatewayEntity = firstGatewayEntity;  
        Integer nextHandlerId = null;  
        GatewayHandler tempGatewayHandler = firstGatewayHandler;  
        // 迭代遍历所有handler,以及将它们链接起来  
        while ((nextHandlerId = tempGatewayEntity.getNextHandlerId()) != null) {  
            GatewayEntity gatewayEntity = gatewayDao.getGatewayEntity(nextHandlerId);  
            GatewayHandler gatewayHandler = newGatewayHandler(gatewayEntity);  
            tempGatewayHandler.setNext(gatewayHandler);  
            tempGatewayHandler = gatewayHandler;  
            tempGatewayEntity = gatewayEntity;  
        }  
    // 返回第一个handler  
        return firstGatewayHandler;  
    }  
  
    /**  
     * 反射实体化具体的处理者  
     * @param firstGatewayEntity  
     * @return  
     */  
    private static GatewayHandler newGatewayHandler(GatewayEntity firstGatewayEntity) {  
        // 获取全限定类名  
        String className = firstGatewayEntity.getConference();   
        try {  
            // 根据全限定类名,加载并初始化该类,即会初始化该类的静态段  
            Class<?> 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();  
    }  
}  
           

epilogue

There are many design patterns, and the chain of responsibility is just one of them, which I think is very interesting and worth learning. Design patterns are indeed an art, and there is still work to be done!

Source: blog.csdn.net/q1472750149/article/details/121886327
Ali

Read on