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