[easily understand design mode] the responsibility chain mode used by large factories

I'm kangarooking, not my brother's brother. An Internet worm "gua" cow, climb up step by step with you, and one day there will be a day for us. Pay attention to me and make a little progress every day ❗ ❗ ❗

Foreword (flicker)

After reading the source code of some open source frameworks, I found that the responsibility chain model is like a XXX (the word here is poor, and the bosses help me fill in the word 0.0 in the comment area), which is applied to all major frameworks. And there are many ways to achieve it. Take a good look at the following contents and learn them well. During the interview, let you have more moves with the interviewer, and let the leaders look at you with new eyes in the work.

First acquaintance

Responsibility chain: it is mainly used for process control and processing, such as the approval process of asking for leave, the assembly line in the factory, the pipeline of devops, etc., which can be regarded as the responsibility chain.

Introduction (nonsense)

Definition of Chain of Responsibility mode: in order to avoid coupling the request sender with multiple request handlers, all request handlers are connected into a chain by remembering the reference of the next object through the previous object; When a request occurs, it can be passed along the chain until an object processes it.

In the responsibility chain mode, customers only need to send the request to the responsibility chain. They don't need to care about the processing details of the request and the transmission process of the request. The request will be transmitted automatically. Therefore, the responsibility chain decouples the sender of the request from the handler of the request.

The responsibility chain model is an object behavior model, and its main advantages are as follows.

  • Reduces the coupling between objects. This mode makes an object do not need to know which object handles its request and the structure of the chain, and the sender and receiver do not need to have the clear information of each other.
  • It enhances the scalability of the system. New request processing classes can be added as needed to meet the opening and closing principle.
  • Increased flexibility in assigning responsibilities to objects. When the workflow changes, you can dynamically change the members in the chain or transfer their order, or dynamically add or delete responsibilities.
  • The chain of responsibility simplifies the connection between objects. Each object only needs to maintain a reference to its successor, without maintaining the references of all other processors, which avoids the use of many if or if ·· else statements.
  • Responsibility sharing. Each class only needs to deal with its own work that should be handled, and the work that should not be handled should be transferred to the next object for completion. The scope of responsibility of each class should be clarified and in line with the principle of single responsibility of the class.

Its main disadvantages are as follows.

  • There is no guarantee that every request will be processed. Since a request has no specific receiver, it cannot be guaranteed that it will be processed. The request may not be processed until it reaches the end of the chain.
  • The processing chain of requests is long, which may affect the performance of the system.
  • The rationality of the establishment of responsibility chain depends on the client, which increases the complexity of the client and may lead to system errors due to the wrong setting of responsibility chain, such as circular call.

Quoted from: http://c.biancheng.net/view/1...

Purpose: decouple the requestor from the handler and call in a chain, which can flexibly change the processing order, comply with the opening and closing principle and have strong scalability.

Proceed from reality

Scenario: the company needs to implement a process approval function, such as leave process approval, overtime approval, reimbursement approval, etc.

Three elements:

  1. Responsibility chain (approval process)
  2. Handled by (approver)
  3. Information transmitted in the chain (approval content)

Note: abstract the elements that may be extended

Code example

Code address: https://gitee.com/kangarookin...

Define the abstract class of the approver. "The usual routine of design pattern defines the classes that may have multiple implementations as abstract to improve program scalability". In this example, the approver may include HR, manager, technical director, team leader and so on

/**
 * Nodes in the responsibility chain (approvers in the process)
 * @param <T>
 * Here, the approved information is defined as generic, which is more flexible
 */
@Data
public abstract class ApplyHandler<T> {
    protected ApplyHandler<?> next = null;
    //Define the approval sequence of the approver in the whole responsibility chain
    protected Integer order;
    protected String name;

    public void apply(Object obj) {
        if (null != next) {
            next.transform(obj);
        }else {
            System.out.println("-----------------------------");
            System.out.println("end");
        }
    }

    @SuppressWarnings("unchecked")
    public void transform(Object obj){
        //Converts an incoming object to a generic defined type
        T param = (T)obj;
        handle(param);
    }

    //Specific approval logic
    protected abstract void handle(T param);

    public ApplyHandler(Integer order) {
        this.order = order;
    }

    public ApplyHandler() {
    }
}

Create an abstract responsibility chain (approval process) and create a default chain (default approval process). Provide an extensible opening for other types of approval processes created later.

/**
 * Manager of responsibility chain
 * Manage the head and tail of the chain, as well as the addition, deletion, modification and inspection of the chain
 */
@Data
public abstract class ApplyChain {
    public ApplyHandler<?> first = new ApplyHandler<Object>() {
        @Override
        public void handle(Object obj) {
            //The first node does not participate in processing, but applies directly to the next node
            apply(obj);
        }
    };
    private ApplyHandler<?> end = first;

    //Call entry of responsibility chain
    protected abstract void exc(Object obj);

    //Add node to tail
    public void addLast(ApplyHandler<?> handler){
        end.setNext(handler);
        end = handler;
    }

    //... Other methods of operating the chain of responsibility are omitted here
}

/**
 * Default approval process
 */
class DefaultApplyChain extends ApplyChain {
    @Override
    protected void exc(Object obj) {
        //Use the chain head to start calling the whole responsibility chain
        System.out.println("--------------- start --------------");
        first.transform(obj);
    }
}

Define different actual approvers

public class GroupLeaderHandler extends ApplyHandler<LeaveApplyMsg> {
    public GroupLeaderHandler(Integer order) {
        super(order);
    }
    @Override
    protected void handle(LeaveApplyMsg applyMsg) {
        System.out.println("Group leader approval");
        //Continue to apply downward after approval
        apply(applyMsg);
    }
}

class ManagerHandler extends ApplyHandler<LeaveApplyMsg> {
    public ManagerHandler(Integer order) {
        super(order);
    }
    @Override
    protected void handle(LeaveApplyMsg applyMsg) {
        System.out.println("Manager approval");
        //Continue to apply downward after approval
        apply(applyMsg);
    }
}

class HrHandler extends ApplyHandler<LeaveApplyMsg> {
    public HrHandler(Integer order) {
        super(order);
    }
    @Override
    protected void handle(LeaveApplyMsg applyMsg) {
        System.out.println("HR Examine and approve");
        //Continue to apply downward after approval
        apply(applyMsg);
    }
}

Define leave information

/**
 * Leave approval information
 */
@Data
public class LeaveApplyMsg {
    private String msg;
    private Integer level;
    private String name;
    private Integer hour;
}

Simulate users to submit leave application

public class ChainClient {
    public static void main(String[] args) {
        //The list can be queried from a table in the database
        List<ApplyHandler<LeaveApplyMsg>> applyHandlerList = new ArrayList<>();
        ApplyHandler<LeaveApplyMsg> hr = new HrHandler(3);
        ApplyHandler<LeaveApplyMsg> gl = new GroupLeaderHandler(1);
        ApplyHandler<LeaveApplyMsg> m = new ManagerHandler(2);
        applyHandlerList.add(m);
        applyHandlerList.add(gl);
        applyHandlerList.add(hr);
        //Sort by order field
        List<ApplyHandler<LeaveApplyMsg>> collect = applyHandlerList.stream().sorted(Comparator.comparing(ApplyHandler::getOrder)).collect(Collectors.toList());
        ApplyChain applyChain = new DefaultApplyChain();
        //Circular assembly into the chain of responsibility
        for (ApplyHandler<?> applyHandler : collect) {
            applyChain.addLast(applyHandler);
        }
        //In the actual scenario, the above code can be put into the method of initializing data when the system starts. spring has many such extension points, which can be understood by itself

        //The following code is equivalent to that the user selects the approval type (corresponding to different information) at the front end, and then clicks apply
        //The backend triggers applychain exc(applyMsg); This method begins to implement the whole chain of responsibility
        LeaveApplyMsg applyMsg = new LeaveApplyMsg();
        applyMsg.setMsg("apply leave");
        applyMsg.setName("lhm");
        applyMsg.setLevel(1);
        applyChain.exc(applyMsg);

    }
}

Note: for the above description of responsibility chain, refer to sentinel's use of responsibility chain (one-way linked list). Of course, you can also use list.

Use of responsibility chain in open source framework

In the spring cloud gateway, the request filtering uses the responsibility chain mode, and the entry is in the FilteringWebHandler class.
The calling of each plug-in in shenyu gateway also uses the responsibility chain mode, and the entry is in the shenyu webhandler class.
The responsibility chain mode is used for calling each slot in sentinel. The following line of code of CtSph#asyncEntryWithPriorityInternal method can be viewed at the entry

Partners who are interested in the implementation of these modes in the chain can see how to realize these modes. It is easy to understand the use of sentinel by reading the sample code. gateway and shenyu are gateways. They implement the responsibility chain in a similar way. They use responsive programming. We can see the implementation ideas.

The following is the implementation of spring cloud gateway

private static class DefaultGatewayFilterChain implements GatewayFilterChain {
        //Controls which filter in the current list is used for processing
        private final int index;
        //Store all filter s (that is, this implementation adopts list as the responsibility chain)
        private final List<GatewayFilter> filters;

        DefaultGatewayFilterChain(List<GatewayFilter> filters) {
            this.filters = filters;
            this.index = 0;
        }

        private DefaultGatewayFilterChain(DefaultGatewayFilterChain parent, int index) {
            this.filters = parent.getFilters();
            this.index = index;
        }

        public List<GatewayFilter> getFilters() {
            return filters;
        }
        
        //Entry of chain call
        @Override
        public Mono<Void> filter(ServerWebExchange exchange) {
            return Mono.defer(() -> {
                //Judge whether the call is completed
                if (this.index < filters.size()) {
                    //Get the node to be called according to the index
                    GatewayFilter filter = filters.get(this.index);
                   //Create a new chain
                   DefaultGatewayFilterChain chain = new DefaultGatewayFilterChain(this,
                            this.index + 1);
                    //Call the actual processing logic of filter
                    return filter.filter(exchange, chain);
                }
                else {
                    return Mono.empty(); // complete
                }
            });
        }

    }

View the filter logic of one of the GatewayFilter implementation classes (the code is in the StripPrefixGatewayFilterFactory class)

You can see that by calling filter filter(exchange, chain); Method handles the actual filter logic, and finally calls chain The filter method returns to the entrance of the chain call. In this way, a circular call is generated. By changing the value of index, the filter objects in the list are obtained one by one. In this way, the chain call to each filter in the filters list is realized.

summary

The responsibility chain model is applied in many frameworks. Learning this model is helpful to understand the framework source code. When there is a demand for business, the responsibility chain model can also be considered according to the actual situation. It is not difficult to see that design pattern is just an idea and there is no fixed writing method. Therefore, different frameworks may have different implementation methods (depending on the actual project situation). I hope that after reading this article, the little partners can also have their own thinking and realize a responsibility chain mode that belongs to your own writing method!

A little experience of learning design patterns: at the beginning of learning design patterns, I always feel that every time I learn to understand and write my own code, but I will soon forget. In the end, you don't know him and he doesn't know your state, and some design patterns are easy to be confused. How can we master all kinds of design patterns??? I want to know how to pay attention to the next episode_ 0

At present, there have been two issues of easily understanding the design mode. I am still in the process of continuous improvement and learning. It is a little difficult to make the content easy to understand without losing depth. This article has also been revised and modified for many editions. I hope my brothers can give some valuable comments in the comment area.

The official account of WeChat is "kangaroo's Inn". Your three links are my feed. give the thumbs-up 👍 follow ❤️ share 👥

Keywords: Java Programming Design Pattern

Added by mattonline on Sat, 12 Feb 2022 10:07:06 +0200