Why Overusing the Chain of Responsibility Bloats Your Code—and How to Refactor It
This article explains the Chain of Responsibility design pattern, shows its typical use cases, demonstrates how naïve implementations can lead to tangled, hard‑to‑maintain code, and provides step‑by‑step refactorings—including abstract handlers, linked chains, and a factory‑based configuration—to produce clean, extensible Java solutions.
What is the Chain of Responsibility
The Chain of Responsibility is a behavioral design pattern that lets you pass a request along a chain of handlers; each handler can either process the request or forward it to the next handler.
Use Cases
Multi‑condition flow control such as permission checks
ERP workflow approvals (e.g., manager → HR manager → project manager)
Underlying implementation of Java servlet filters
Counterexample
A naive implementation of a three‑level game where each level is checked with nested if statements quickly becomes unreadable and hard to maintain.
public class FirstPassHandler {
public int handler() {
System.out.println("第一关-->FirstPassHandler");
return 80;
}
}
public class SecondPassHandler {
public int handler() {
System.out.println("第二关-->SecondPassHandler");
return 90;
}
}
public class ThirdPassHandler {
public int handler() {
System.out.println("第三关-->ThirdPassHandler,这是最后一关啦");
return 95;
}
}
public class HandlerClient {
public static void main(String[] args) {
FirstPassHandler first = new FirstPassHandler();
SecondPassHandler second = new SecondPassHandler();
ThirdPassHandler third = new ThirdPassHandler();
int firstScore = first.handler();
if (firstScore >= 80) {
int secondScore = second.handler();
if (secondScore >= 90) {
third.handler();
}
}
}
}If the game had 100 levels, the code would explode with deeply nested if blocks, making future changes risky.
Initial Refactor
Introduce a linked list of handlers where each handler knows its successor, eliminating the need for multiple nested conditionals.
public class FirstPassHandler {
private SecondPassHandler secondPassHandler;
public void setSecondPassHandler(SecondPassHandler handler) { this.secondPassHandler = handler; }
private int play() { return 80; }
public int handler() {
System.out.println("第一关-->FirstPassHandler");
if (play() >= 80 && secondPassHandler != null) {
return secondPassHandler.handler();
}
return 80;
}
}
public class SecondPassHandler {
private ThirdPassHandler thirdPassHandler;
public void setThirdPassHandler(ThirdPassHandler handler) { this.thirdPassHandler = handler; }
private int play() { return 90; }
public int handler() {
System.out.println("第二关-->SecondPassHandler");
if (play() >= 90 && thirdPassHandler != null) {
return thirdPassHandler.handler();
}
return 90;
}
}
public class ThirdPassHandler {
private int play() { return 95; }
public int handler() {
System.out.println("第三关-->ThirdPassHandler,这是最后一关啦");
return play();
}
}
public class HandlerClient {
public static void main(String[] args) {
FirstPassHandler first = new FirstPassHandler();
SecondPassHandler second = new SecondPassHandler();
ThirdPassHandler third = new ThirdPassHandler();
first.setSecondPassHandler(second);
second.setThirdPassHandler(third);
first.handler();
}
}Drawbacks of the Initial Refactor
Each concrete handler still declares a specific next‑handler field, making the chain hard to modify.
Extension and maintenance become cumbersome.
Chain Refactor with Abstract Handler
Define an abstract base class that holds a reference to the next handler of the same type, allowing any concrete handler to be linked uniformly.
public abstract class AbstractHandler {
protected AbstractHandler next;
public void setNext(AbstractHandler next) { this.next = next; }
public abstract int handler();
}
public class FirstPassHandler extends AbstractHandler {
private int play() { return 80; }
@Override
public int handler() {
System.out.println("第一关-->FirstPassHandler");
int score = play();
if (score >= 80 && next != null) {
return next.handler();
}
return score;
}
}
public class SecondPassHandler extends AbstractHandler {
private int play() { return 90; }
@Override
public int handler() {
System.out.println("第二关-->SecondPassHandler");
int score = play();
if (score >= 90 && next != null) {
return next.handler();
}
return score;
}
}
public class ThirdPassHandler extends AbstractHandler {
private int play() { return 95; }
@Override
public int handler() {
System.out.println("第三关-->ThirdPassHandler");
int score = play();
if (score >= 95 && next != null) {
return next.handler();
}
return score;
}
}
public class HandlerClient {
public static void main(String[] args) {
FirstPassHandler first = new FirstPassHandler();
SecondPassHandler second = new SecondPassHandler();
ThirdPassHandler third = new ThirdPassHandler();
first.setNext(second);
second.setNext(third);
first.handler();
}
}Factory Refactor Using Enum Configuration
Store handler metadata in an enum and build the chain dynamically via reflection, allowing the chain to be reconfigured without code changes.
public enum GatewayEnum {
API_HANDLER(new GatewayEntity(1, "api接口限流", "cn.dgut.design.chain_of_responsibility.GateWay.impl.ApiLimitGatewayHandler", null, 2)),
BLACKLIST_HANDLER(new GatewayEntity(2, "黑名单拦截", "cn.dgut.design.chain_of_responsibility.GateWay.impl.BlacklistGatewayHandler", 1, 3)),
SESSION_HANDLER(new GatewayEntity(3, "用户会话拦截", "cn.dgut.design.chain_of_responsibility.GateWay.impl.SessionGatewayHandler", 2, null));
private final GatewayEntity gatewayEntity;
GatewayEnum(GatewayEntity entity) { this.gatewayEntity = entity; }
public GatewayEntity getGatewayEntity() { return gatewayEntity; }
}
public class GatewayHandlerEnumFactory {
private static final GatewayDao gatewayDao = new GatewayImpl();
public static GatewayHandler getFirstGatewayHandler() {
GatewayEntity first = gatewayDao.getFirstGatewayEntity();
GatewayHandler firstHandler = newGatewayHandler(first);
if (firstHandler == null) return null;
GatewayEntity cur = first;
GatewayHandler curHandler = firstHandler;
Integer nextId;
while ((nextId = cur.getNextHandlerId()) != null) {
GatewayEntity nextEntity = gatewayDao.getGatewayEntity(nextId);
GatewayHandler nextHandler = newGatewayHandler(nextEntity);
curHandler.setNext(nextHandler);
curHandler = nextHandler;
cur = nextEntity;
}
return firstHandler;
}
private static GatewayHandler newGatewayHandler(GatewayEntity entity) {
try {
Class<?> clazz = Class.forName(entity.getConference());
return (GatewayHandler) clazz.getDeclaredConstructor().newInstance();
} catch (Exception e) { e.printStackTrace(); }
return null;
}
}Conclusion
Design patterns are an art; the Chain of Responsibility is just one of many useful patterns that, when applied appropriately, can greatly improve code readability and extensibility.
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.
Java Backend Technology
Focus on Java-related technologies: SSM, Spring ecosystem, microservices, MySQL, MyCat, clustering, distributed systems, middleware, Linux, networking, multithreading. Occasionally cover DevOps tools like Jenkins, Nexus, Docker, and ELK. Also share technical insights from time to time, committed to Java full-stack development!
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.
