21 Behavioral model-Chain of responsibility model

1 Introduction to chain of responsibility model

2 Principle of chain of responsibility model


3 Implementation of chain of responsibility model

The implementation of the chain of responsibility pattern is very simple. Each specific processing class will be saved in the next processing class after it. When the processing is completed, the next set processing class will be called until the last processing class no longer sets the next processing class, and then the processing chain is completed.

public class RequestData{<!-- -->
private String data;
\t
public RequestData(String data) {<!-- -->
this.data = data;
}
public String getData() {<!-- -->
return data;
}
public void setData(String data) {<!-- -->
this.data = data;
}
}
/**
 * Abstract handler class
 **/
public abstract class Handler {<!-- -->

    //Reference of subsequent processor
    protected Handler successor;

    public void setSuccessor(Handler successor) {<!-- -->
        this.successor = successor;
    }

    public abstract void handle(RequestData requestData);
}
public class HandlerA extends Handler {<!-- -->

    @Override
    public void handle(RequestData requestData) {<!-- -->

        System.out.println("HandlerA executes code logic! Processing: " + requestData.getData());
        requestData.setData(requestData.getData().replace("A",""));
        //Continue to call the processor backward when judging
        if(successor != null){<!-- -->
            successor.handle(requestData);
        }else{<!-- -->
            System.out.println("Execution aborted");
        }
    }
}
public class HandlerB extends Handler {<!-- -->

    @Override
    public void handle(RequestData requestData) {<!-- -->

        System.out.println("HandlerB executes code logic! Processing: " + requestData.getData());
        requestData.setData(requestData.getData().replace("B",""));
        //Continue to call the processor backward when judging
        if(successor != null){<!-- -->
            successor.handle(requestData);
        }else{<!-- -->
            System.out.println("Execution aborted");
        }
    }
}
public class HandlerC extends Handler {<!-- -->

    @Override
    public void handle(RequestData requestData) {<!-- -->

        System.out.println("HandlerC executes code logic! Processing: " + requestData.getData());
        requestData.setData(requestData.getData().replace("C",""));
        //Continue to call the processor backward when judging
        if(successor != null){<!-- -->
            successor.handle(requestData);
        }else{<!-- -->
            System.out.println("Execution aborted");
        }
    }
}
public class Client {<!-- -->

    public static void main(String[] args) {<!-- -->
        Handler h1 = new HandlerA();
        Handler h2 = new HandlerB();
        Handler h3 = new HandlerC();

        //Create processing chain
        h1.setSuccessor(h2);
        h2.setSuccessor(h3);

        RequestData requestData = new RequestData("Request data: ABCD");
        //Call the method that handles the head of the chain
        h1.handle(requestData);
    }
}
4 Examples of application of chain of responsibility model


1) Not using design patterns
/**
 * Package audit information
 **/
public class AuthInfo {<!-- -->

    private String code; //status code

    private String info = ""; //Audit related information

    public AuthInfo(String code, String... infos) {<!-- -->
        this.code = code;
        for (String str : infos) {<!-- -->
            this.info = info.concat(str + " ");
        }
    }

    public String getCode() {<!-- -->
        return code;
    }

    public void setCode(String code) {<!-- -->
        this.code = code;
    }

    public String getInfo() {<!-- -->
        return info;
    }

    public void setInfo(String info) {<!-- -->
        this.info = info;
    }

    @Override
    public String toString() {<!-- -->
        return "AuthInfo{" +
                "code='" + code + ''' +
                ", info='" + info + ''' +
                '}';
    }
}
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * Simulation audit service
 **/
public class AuthService {<!-- -->

    //Audit information container key: Approver Id + Approval Order Id, value: Approval time
    private static Map<String, Date> authMap = new HashMap<>();


    /**
     * Audit method
     * @param uId reviewer id
     * @param orderId review order id
     */
    public static void auth(String uId, String orderId){<!-- -->
        System.out.println("Enter the approval process, approver ID: " + uId);
        authMap.put(uId.concat(orderId),new Date());
    }

    /**
     * Query audit results
     * @param uId
     * @param orderId
     * @return: java.util.Date
     */
    public static Date queryAuthInfo(String uId, String orderId){<!-- -->
        return authMap.get(uId.concat(orderId));
    }
}
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * Review application interface
 **/
public class AuthController {<!-- -->

    /**
     * Audit method
     * @param name Applicant name
     * @param orderId application order ID
     * @param authDate application time
     * @return: AuthInfo
     */
    public AuthInfo doAuth(String name, String orderId, Date authDate) throws ParseException {<!-- -->

        //Third level approval
        Date date = null;

        //Query whether there is audit information, virtual third-level auditor ID: 1000013
        date = AuthService.queryAuthInfo("1000013", orderId);
        if(date == null){<!-- -->
            return new AuthInfo("0001","Order number: " + orderId,
                    "Status: Waiting for approval from the third-level approval manager");
        }


        /**
         *Level 2 approval
         * Query whether audit information exists, virtual secondary auditor ID: 1000012
         * The time range for the second-level reviewer to review the application form is: 11-01 ~ 11-10
         */
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        if(authDate.after(sdf.parse("2022-10-31 00:00:00")) & amp; & amp; authDate.before(sdf.parse("2022-11-11 00:00:00" )) ){<!-- -->
            //Condition is established, query the secondary review information
            date = AuthService.queryAuthInfo("1000012",orderId);

            if(date == null){<!-- -->
                return new AuthInfo("0001","Order number: " + orderId,
                        "Status: Waiting for approval by the second-level approval person in charge");
            }
        }

        /**
         *First level approval
         * Query whether audit information exists, virtual secondary auditor ID: 1000012
         * The time range for the second-level reviewer to review the application form is: 11-11 ~ 11-31
         */
        if(authDate.after(sdf.parse("2022-11-10 00:00:00")) & amp; & amp; authDate.before(sdf.parse("2022-11-31 00:00:00" )) ){<!-- -->
            //Condition is established, query the secondary review information
            date = AuthService.queryAuthInfo("1000011",orderId);

            if(date == null){<!-- -->
                return new AuthInfo("0001","Order number: " + orderId,
                        "Status: Waiting for approval from the first-level approval manager");
            }
        }

        return new AuthInfo("0001","Order number: " + orderId,"Applicant: " + name," Status: Approval completed!");
    }

}
public class Client {<!-- -->

    public static void main(String[] args) throws ParseException {<!-- -->

        AuthController controller = new AuthController();

        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Date date = sdf.parse("2022-11-06 00:00:00");

        //Simulate review request and approval operations
        AuthInfo info1 = controller.doAuth("R&D Xiao Zhou", "100001000010000", date);
        System.out.println("Current audit status: " + info1.getInfo());

        AuthService.auth("1000013","100001000010000");
        System.out.println("Approval completed by the third-level approval manager, approver: Wang Gong");

        System.out.println("============================================ ======");

        AuthInfo info2 = controller.doAuth("R&D Xiao Zhou", "100001000010000", date);
        System.out.println("Current audit status: " + info2.getInfo());

        AuthService.auth("1000012","100001000010000");
        System.out.println("The approval by the second-level approval person in charge is completed, the approver is: Manager Zhou");


        System.out.println("============================================ ======");

        AuthInfo info3 = controller.doAuth("R&D Xiao Zhou", "100001000010000", date);
        System.out.println("Current audit status: " + info3.getInfo());

        AuthService.auth("1000012","100001000010000");
        System.out.println("Approval completed by the first-level approval manager, approver: Mr. Luo");
    }
}
2) Code refactoring in chain of responsibility mode

The figure below is the responsibility chain structure designed for the current business. There are three subclasses under the unified abstract class AuthLink.
The execution of subclasses is orchestrated to simulate a link, which is the chain of responsibility in the business.

/**
 * Abstract audit chain class
 **/
public abstract class AuthLink {<!-- -->

    protected Logger logger = (Logger) LoggerFactory.getLogger(AuthLink.class);

    protected SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    protected String levelUserId; //auditor id

    protected String levelUserName; //Name of reviewer

    protected AuthLink next; //Indicates holding a reference to the next processing object

    public AuthLink(String levelUserId, String levelUserName) {<!-- -->
        this.levelUserId = levelUserId;
        this.levelUserName = levelUserName;
    }

    //Get the next processor
    public AuthLink getNext() {<!-- -->
        return next;
    }

    //Add a processor to the chain of responsibility
    public AuthLink appendNext(AuthLink next){<!-- -->
        this.next = next;
        return this;
    }

    //Abstract audit method
    public abstract AuthInfo doAuth(String uId, String orderId, Date authDate);
}
public class Level1AuthLink extends AuthLink {<!-- -->

    private Date beginDate = sdf.parse("2022-11-11 00:00:00");
    private Date endDate = sdf.parse("2022-11-31 00:00:00");

    public Level1AuthLink(String levelUserId, String levelUserName) throws ParseException {<!-- -->
        super(levelUserId, levelUserName);
    }

    @Override
    public AuthInfo doAuth(String uId, String orderId, Date authDate) {<!-- -->

        Date date = AuthService.queryAuthInfo(levelUserId, orderId);

        if(null == date){<!-- -->
            return new AuthInfo("0001","Order number: " +
                    orderId,"Status: awaiting approval by first-level reviewer", levelUserName);
        }

        AuthLink next = super.getNext();

        if(next == null){<!-- -->
            return new AuthInfo("0001","Order number: " +
                    orderId, "Status: Level 1 approval completed", "Approver: " + levelUserName);
        }

        if(authDate.before(beginDate) || authDate.after(endDate)){<!-- -->
            return new AuthInfo("0001","Order number: " +
                    orderId,"Status: Level 1 approval completed", "Approver: " + levelUserName);
        }

        return next.doAuth(uId,orderId,authDate);
    }
}
public class Level2AuthLink extends AuthLink {<!-- -->

    private Date beginDate = sdf.parse("2022-11-11 00:00:00");
    private Date endDate = sdf.parse("2022-11-31 00:00:00");

    public Level2AuthLink(String levelUserId, String levelUserName) throws ParseException {<!-- -->
        super(levelUserId, levelUserName);
    }

    @Override
    public AuthInfo doAuth(String uId, String orderId, Date authDate) {<!-- -->

        Date date = AuthService.queryAuthInfo(levelUserId, orderId);

        if(null == date){<!-- -->
            return new AuthInfo("0001","Order number: " +
                    orderId,"Status: To be approved by the second-level reviewer", levelUserName);
        }

        AuthLink next = super.getNext();

        if(next == null){<!-- -->
            return new AuthInfo("0000","Order number: " +
                    orderId,"Status: Level 2 approval completed", "Approver: " + levelUserName);
        }

        if(authDate.before(beginDate) || authDate.after(endDate)){<!-- -->
            return new AuthInfo("0000","Order number: " +
                    orderId,"Status: Level 2 approval completed", "Approver: " + levelUserName);
        }

        return next.doAuth(uId,orderId,authDate);
    }
}
public class Level3AuthLink extends AuthLink {<!-- -->

    public Level3AuthLink(String levelUserId, String levelUserName) throws ParseException {<!-- -->
        super(levelUserId, levelUserName);
    }

    @Override
    public AuthInfo doAuth(String uId, String orderId, Date authDate) {<!-- -->

        Date date = AuthService.queryAuthInfo(levelUserId, orderId);

        if(null == date){<!-- -->
            return new AuthInfo("0001","Order number: " +
                    orderId,"Status: awaiting approval by level 3 reviewer", levelUserName);
        }

        AuthLink next = super.getNext();

        if(next == null){<!-- -->
            return new AuthInfo("0000","Order number: " +
                    orderId, "Status: Level 3 approval completed", "Approver: " + levelUserName);
        }

        return next.doAuth(uId,orderId,authDate);
    }
}
public class Client {<!-- -->

    private Logger logger = LoggerFactory.getLogger(Client.class);

    @Test
    public void test_Auth() throws ParseException {<!-- -->

        //Define the chain of responsibility
        AuthLink authLink = new Level3AuthLink("1000013", "Li Gong")
                .appendNext(new Level2AuthLink("1000012", "Manager Wang")
                        .appendNext(new Level1AuthLink("1000011", "Mr. Luo")));

        SimpleDateFormat f = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Date currentDate = f.parse("2022-11-18 23:49:46");

        logger.info("Test result: {}", JSON.toJSONString(authLink.doAuth("R&D Cow Horse", "1000998004813441", currentDate)));

// // Simulate the approval of the third-level person in charge
        AuthService.auth("1000013", "1000998004813441");
        logger.info("Test result: {}", "Simulating the approval of the third-level person in charge, Mr. Wang");
        logger.info("Test result: {}", JSON.toJSONString(authLink.doAuth("R&D Cow Horse", "1000998004813441", currentDate)));

// // Simulate the approval of the second-level person in charge
        AuthService.auth("1000012", "1000998004813441");
        logger.info("Test result: {}", "Simulating the approval of the second-level person in charge, Manager Zhang");
        logger.info("Test result: {}", JSON.toJSONString(authLink.doAuth("R&D Cow Horse", "1000998004813441", currentDate)));

// // Simulate the approval of the first-level person in charge
        AuthService.auth("1000011", "1000998004813441");
        logger.info("Test result: {}", "Simulating the approval of the first-level person in charge, Mr. Duan");
        logger.info("Test result: {}", JSON.toJSONString(authLink.doAuth("R&D Cow Horse", "1000998004813441", currentDate)));

    }
}

5 Summary of chain of responsibility model