Transform IF Chains into Scalable Java: Master the Responsibility Pattern
This article explains the Chain of Responsibility design pattern in Java, illustrating its use for multi‑level approval workflows during high‑traffic events, provides step‑by‑step code implementations, refactors naive IF logic into modular link classes, and demonstrates testing, highlighting benefits for clean, extensible backend architecture.
Introduction
Programming development, like shooting or skiing, requires a real environment to practice; similarly, system reliability is measured by metrics such as QPS, TPS, TP99, TP999, availability, and response time. Without exposure to such technical scenarios, developers cannot fully grasp system behavior.
Development Environment
JDK 1.8
IDEA + Maven
Source code: https://github.com/fuzhengwei/itstack-demo-design
itstack-demo-design-13-00 – scenario simulation project; simulates an online approval interface.
itstack-demo-design-13-01 – implements business requirements with a single code block.
itstack-demo-design-13-02 – refactors code using design patterns for comparative learning.
Chain of Responsibility Pattern Overview
The pattern solves the sequential processing relationship among a set of services, similar to a multi‑level approval workflow where different amounts require different approvers.
Scenario Simulation
This case simulates the approval process for a 618 promotion launch, where higher traffic periods require additional approval levels.
Project Structure
itstack-demo-design-13-00
└── src
└── main
└── java
└── org.itstack.demo.design
└── AuthService.javaAuthService provides two static methods: queryAuthInfo to check approval status and auth to record an approval in an in‑memory map.
Direct IF Implementation
Project Structure
itstack-demo-design-13-01
└── src
└── main
└── java
└── org.itstack.demo.design
└── AuthController.javaAuthController uses nested if statements to handle three approval levels based on date ranges, returning an AuthInfo object with the current status.
Testing
@Test
public void test_AuthController() throws ParseException {
AuthController authController = new AuthController();
// simulate third‑level approval
logger.info("Result: {}", JSON.toJSONString(authController.doAuth("User","Order", new Date())));
AuthService.auth("1000013", "Order");
// simulate second‑level approval
logger.info("Result: {}", JSON.toJSONString(authController.doAuth("User","Order", new Date())));
AuthService.auth("1000012", "Order");
// simulate first‑level approval
logger.info("Result: {}", JSON.toJSONString(authController.doAuth("User","Order", new Date())));
AuthService.auth("1000011", "Order");
logger.info("Result: {}", JSON.toJSONString(authController.doAuth("User","Order", new Date())));
}The test shows each approval step returning a status like “waiting for third‑level approver”, then “waiting for second‑level”, and finally “approval completed”.
Refactoring with Chain of Responsibility
We replace the nested if logic with a chain of link objects, each handling a specific approval level.
Project Structure
itstack-demo-design-13-02
└── src
└── main
└── java
└── org.itstack.demo.design
├── impl
│ ├── Level1AuthLink.java
│ ├── Level2AuthLink.java
│ └── Level3AuthLink.java
├── AuthInfo.java
└── AuthLink.javaCore Classes
public class AuthInfo {
private String code;
private String info = "";
public AuthInfo(String code, String... infos) {
this.code = code;
for (String str : infos) {
this.info = this.info.concat(str);
}
}
// getters and setters omitted
} public abstract class AuthLink {
protected Logger logger = LoggerFactory.getLogger(AuthLink.class);
protected SimpleDateFormat f = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
protected String levelUserId;
protected String levelUserName;
private AuthLink next;
public AuthLink(String levelUserId, String levelUserName) {
this.levelUserId = levelUserId;
this.levelUserName = levelUserName;
}
public AuthLink next() { return next; }
public AuthLink appendNext(AuthLink next) { this.next = next; return this; }
public abstract AuthInfo doAuth(String uId, String orderId, Date authDate);
} public class Level1AuthLink extends AuthLink {
public Level1AuthLink(String levelUserId, String levelUserName) { super(levelUserId, levelUserName); }
public AuthInfo doAuth(String uId, String orderId, Date authDate) {
Date date = AuthService.queryAuthInfo(levelUserId, orderId);
if (date == null) {
return new AuthInfo("0001", "Order:", orderId, " Status: waiting for first‑level approver ", levelUserName);
}
AuthLink next = super.next();
if (next == null) {
return new AuthInfo("0000", "Order:", orderId, " Status: first‑level approved ", " Time:", f.format(date), " Approver:", levelUserName);
}
return next.doAuth(uId, orderId, authDate);
}
} public class Level2AuthLink extends AuthLink {
private Date beginDate = f.parse("2020-06-11 00:00:00");
private Date endDate = f.parse("2020-06-20 23:59:59");
public Level2AuthLink(String levelUserId, String levelUserName) throws ParseException { super(levelUserId, levelUserName); }
public AuthInfo doAuth(String uId, String orderId, Date authDate) {
Date date = AuthService.queryAuthInfo(levelUserId, orderId);
if (date == null) {
return new AuthInfo("0001", "Order:", orderId, " Status: waiting for second‑level approver ", levelUserName);
}
AuthLink next = super.next();
if (next == null) {
return new AuthInfo("0000", "Order:", orderId, " Status: second‑level approved ", " Time:", f.format(date), " Approver:", levelUserName);
}
if (authDate.before(beginDate) || authDate.after(endDate)) {
return new AuthInfo("0000", "Order:", orderId, " Status: second‑level approved ", " Time:", f.format(date), " Approver:", levelUserName);
}
return next.doAuth(uId, orderId, authDate);
}
} public class Level3AuthLink extends AuthLink {
private Date beginDate = f.parse("2020-06-01 00:00:00");
private Date endDate = f.parse("2020-06-25 23:59:59");
public Level3AuthLink(String levelUserId, String levelUserName) throws ParseException { super(levelUserId, levelUserName); }
public AuthInfo doAuth(String uId, String orderId, Date authDate) {
Date date = AuthService.queryAuthInfo(levelUserId, orderId);
if (date == null) {
return new AuthInfo("0001", "Order:", orderId, " Status: waiting for third‑level approver ", levelUserName);
}
AuthLink next = super.next();
if (next == null) {
return new AuthInfo("0000", "Order:", orderId, " Status: third‑level approved ", " Time:", f.format(date), " Approver:", levelUserName);
}
if (authDate.before(beginDate) || authDate.after(endDate)) {
return new AuthInfo("0000", "Order:", orderId, " Status: third‑level approved ", " Time:", f.format(date), " Approver:", levelUserName);
}
return next.doAuth(uId, orderId, authDate);
}
}Testing the Chain
@Test
public void test_AuthLink() throws ParseException {
AuthLink authLink = new Level3AuthLink("1000013", "Wang")
.appendNext(new Level2AuthLink("1000012", "Zhang")
.appendNext(new Level1AuthLink("1000011", "Duan")));
logger.info("Result: {}", JSON.toJSONString(authLink.doAuth("User", "Order", new Date())));
AuthService.auth("1000013", "Order");
logger.info("Result: {}", JSON.toJSONString(authLink.doAuth("User", "Order", new Date())));
AuthService.auth("1000012", "Order");
logger.info("Result: {}", JSON.toJSONString(authLink.doAuth("User", "Order", new Date())));
AuthService.auth("1000011", "Order");
logger.info("Result: {}", JSON.toJSONString(authLink.doAuth("User", "Order", new Date())));
}The test output shows the request moving through third, second, and first level approvers, finally reporting a completed approval, confirming that the chain works correctly and eliminates tangled if statements.
Conclusion
Refactoring from nested if statements to a Chain of Responsibility makes the code cleaner, more extensible, and adheres to the Single Responsibility and Open‑Closed principles. The pattern is suitable for approval workflows and similar sequential processing scenarios, though it should be applied judiciously to avoid unnecessary complexity.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
macrozheng
Dedicated to Java tech sharing and dissecting top open-source projects. Topics include Spring Boot, Spring Cloud, Docker, Kubernetes and more. Author’s GitHub project “mall” has 50K+ stars.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
