This article explains the chain of responsibility design pattern

As the name implies, the Chain of Responsibility Pattern creates a Chain of recipient objects for a request. This pattern decouples the sender and receiver of the request by giving the type of request. This type of design pattern is behavioral. In this pattern, each receiver typically contains a reference to the other receiver. If an object cannot handle the request, it passes the same request to the next recipient, and so on.Copy the code
demand

In the process processing business of the company, there are different process nodes, and different SMS messages need to be sent at different nodes to notify the next node. 1 initiate ->2 review ->3 dispose ->4 results review -> Complete. Each link can also have the function of going back to the previous node, and there may be other process nodes in different regions. At the beginning, the product only proposed that two nodes, audit and disposal, need to send SMS, so the initial code is written like this.


public class WarningSmsHandler {

    public void sendSms(a) {
        // Handle sending SMS logic
        if(Whether the audit is completed) {// Query the handler, set the SMS content, send SMS
        } else if(Whether the disposal is complete) {// Query result reviewer, set SMS content, send SMS}}}Copy the code

As time went on, the product introduced the need to send a text message during the initiation process to notify reviewers. At this time, you can choose to modify the original WarningSmsHandler class, which is faster to implement now, but it will also bring some problems in the future: 1. As more and more nodes need to send SMS messages, codes will be continuously added to this class, which violates the principle of single responsibility. 2. In the future, add a node’s SMS, delete a node’s SMS, need to change the code of WarningSmsHandler class, which violates the open and close principle.

So think about how to choose a comprehensive solution to avoid these problems. Back to the requirement, SMS is sent from one node to the next, generally only to the person at the next node. Here we think of using the responsibility chain mode to split the logic of sending SMS messages from each node into a single responsibility.

Implement this requirement according to the chain of responsibility pattern

1. Define the unified request class

@Data
public class WarningSmsHandlerRequest{

    //通报id
    private Long warningId;
    
    // Next notification status
    private WarningStateEnum nextWarningState;
    
    // Next notify the disposition type
    private WarningHandleUnitEnum warningHandleUnitEnum;
    
    // Add parameters
    private List<String> attachmentParam;
    
    // SMS template ID Some SMS vendors require templates
    private String templateId;
}
Copy the code

2. Define the unified processing result class


@Data
public class WarningSmsHandlerResult {
    // Send a list of successful mobile phone numbers
    private List<String> mobiles =new ArrayList<>();
    // Check whether the message is sent successfully
    private Boolean success;
}

Copy the code

2. Define the request processing interface


/** * SMS processor interface */
public interface SmsHandler {

    // Whether the current interface supports processing
    boolean support(WarningSmsHandlerRequest request);

    // Process logic
    void handleSms(WarningSmsHandlerRequest request, WarningSmsHandlerResult result);
} 
Copy the code

A specific request processor implementation – audit link SMS processor


/** ** /
public class WarningAuditSmsHandler implements SmsHandler {
    @Override
    public boolean support(WarningSmsHandlerRequest request) {
        return request.getNextWarningState().equals(WarningStatusEnum.WAIT_AUDIT);
    }

    @Override
    public void handleSms(WarningSmsHandlerRequest request, WarningSmsHandlerResult result) {
        // Process audit messages
        System.out.println("Handling audit SMS sending logic"); }}Copy the code

A specific request processor implementation – disposal link SMS processor


/** */
public class WarningHandleSmsHandler implements SmsHandler {

    @Override
    public boolean support(WarningSmsHandlerRequest request) {
        return request.getNextWarningState().equals(WarningStatusEnum.HANDLE);
    }

    @Override
    public void handleSms(WarningSmsHandlerRequest request, WarningSmsHandlerResult result) {
        // Process audit messages
        System.out.println("Handle SMS sending logic"); }}Copy the code

Now that the processor is written, why are they strung together? It is generally written that each processor has a method to set the next processor (setNext), specifying which processor will execute next. This notation is commonly used in sequential link processing, and the setNext method needs to be modified in future changes or new processors. In this case, each node invokes the SMS sending process, and each processor has a support method that filters out the processors to be processed based on the current request.

Construct the processor call link port

public interface WarningSmsHandlerInvocation {

    WarningSmsHandlerResult invoke(WarningSmsHandlerRequest request);
}

Copy the code

Construct processor call link port implementation


@Component
@Setter
public class WarningSmsHandlerInvocationImpl implements WarningSmsHandlerInvocation {

    // Inject all SmsHandler type beans through Spring
    @Autowired
    private List<SmsHandler> smsHandlers;

    @Override
    public WarningSmsHandlerResult invoke(WarningSmsHandlerRequest request) {
        WarningSmsHandlerResult result = new WarningSmsHandlerResult();
        // Call all handlers that match the current request in turn
        for (SmsHandler smsHandler : smsHandlers) {
            if(smsHandler.support(request)) { smsHandler.handleSms(request, result); }}returnresult; }}Copy the code

Test using the written handler chain caller

@Component
public class WarningSmsHandlerClient {

    @Autowired
    private WarningSmsHandlerInvocation invocation;

    public void testSmsSend(a) {

        WarningSmsHandlerRequest request = newWarningSmsHandlerRequest(); request.setNextWarningState(WarningStatusEnum.WAIT_AUDIT); WarningSmsHandlerResult result = invocation.invoke(request); System.out.println(result); }}Copy the code

If you need to add a new processor, just implement the WarningAuditSmsHandler interface.


conclusion

By using the chain of responsibility pattern, the coupling of business code can be reduced and the overall extensibility and maintainability can be improved.

  • Each specific processor should only handle its own business, in accordance with the principle of a single responsibility
  • Additions, modifications, and deletions do not modify the logic of the caller

All the above content, if better design welcome to exchange and discuss.