Understanding and Refactoring the Chain of Responsibility Design Pattern in Java
This article explains the Chain of Responsibility pattern, its typical use cases, shows a problematic implementation for a game level system, and demonstrates how to refactor the code using abstract handlers, concrete handlers, and a factory to build a clean, extensible processing chain in Java.
Background
Recently a team member implemented an import feature using the Chain of Responsibility pattern, resulting in overly complex code with many bugs. The author argues that the Template Method would be more appropriate and uses this example to discuss the Chain of Responsibility.
What is the Chain of Responsibility?
The Chain of Responsibility is a behavioral design pattern that allows a request to be passed along a chain of handlers. Each handler can either process the request or forward it to the next handler in the chain.
Typical Scenarios
Multi‑condition flow control, such as permission checks.
ERP workflow approvals (e.g., manager, HR manager, project manager).
Underlying implementation of Java servlet filters.
Anti‑Pattern Example
The article shows a naïve implementation for a three‑level game where each level is represented by a separate class with nested if statements. The code quickly becomes unreadable and hard to maintain, especially when the number of levels grows.
public class FirstPassHandler {
public int handler() {
System.out.println("第一关-->FirstPassHandler");
return 80;
}
}
// ... similar classes for SecondPassHandler and ThirdPassHandler ...
public class HandlerClient {
public static void main(String[] args) {
FirstPassHandler firstPassHandler = new FirstPassHandler();
// instantiate second and third handlers
// set up chain manually
firstPassHandler.handler();
}
}When the number of levels increases, the code expands with repetitive if checks, making modifications risky.
Initial Refactor
By linking each level as a node in a chain, the client no longer needs multiple if statements. Each handler knows its successor and forwards the request when appropriate.
public class FirstPassHandler {
private SecondPassHandler secondPassHandler;
public void setSecondPassHandler(SecondPassHandler secondPassHandler) {
this.secondPassHandler = secondPassHandler;
}
public int handler() {
System.out.println("第一关-->FirstPassHandler");
if (play() >= 80 && secondPassHandler != null) {
return secondPassHandler.handler();
}
return 80;
}
private int play() { return 80; }
}
// ... similar for SecondPassHandler and ThirdPassHandler ...
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 holds a reference to a specific next handler type, reducing flexibility.
Extending the chain requires modifying each handler class.
Improved Refactor with an Abstract Handler
Introduce an abstract AbstractHandler that defines a generic next reference and an abstract handler() method. Concrete handlers extend this class, allowing any handler to be linked to any other.
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;
}
}
// ... SecondPassHandler and ThirdPassHandler similar ...
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‑Based Chain Construction
To decouple the chain configuration from code, the article introduces an enum GatewayEnum that stores handler metadata (ID, name, class name, predecessor and successor IDs). A GatewayDao implementation loads these configurations into a map, and a factory iterates over the map to instantiate handlers via reflection and link them together.
public enum GatewayEnum {
API_HANDLER(new GatewayEntity(1, "api接口限流", "com.example.ApiLimitGatewayHandler", null, 2)),
BLACKLIST_HANDLER(new GatewayEntity(2, "黑名单拦截", "com.example.BlacklistGatewayHandler", 1, 3)),
SESSION_HANDLER(new GatewayEntity(3, "用户会话拦截", "com.example.SessionGatewayHandler", 2, null));
private GatewayEntity gatewayEntity;
GatewayEnum(GatewayEntity entity) { this.gatewayEntity = entity; }
public GatewayEntity getGatewayEntity() { return gatewayEntity; }
}
public class GatewayHandlerEnumFactory {
private static GatewayDao gatewayDao = new GatewayImpl();
public static GatewayHandler getFirstGatewayHandler() {
GatewayEntity firstEntity = gatewayDao.getFirstGatewayEntity();
GatewayHandler firstHandler = newGatewayHandler(firstEntity);
GatewayEntity currentEntity = firstEntity;
GatewayHandler currentHandler = firstHandler;
Integer nextId;
while ((nextId = currentEntity.getNextHandlerId()) != null) {
GatewayEntity nextEntity = gatewayDao.getGatewayEntity(nextId);
GatewayHandler nextHandler = newGatewayHandler(nextEntity);
currentHandler.setNext(nextHandler);
currentHandler = nextHandler;
currentEntity = nextEntity;
}
return firstHandler;
}
private static GatewayHandler newGatewayHandler(GatewayEntity entity) {
try {
Class<?> clazz = Class.forName(entity.getConference());
return (GatewayHandler) clazz.newInstance();
} catch (Exception e) { e.printStackTrace(); }
return null;
}
}Conclusion
The Chain of Responsibility is a powerful pattern for decoupling request senders from processors. By abstracting handlers, using a generic successor reference, and optionally configuring the chain via enums or external files, developers can create maintainable, extensible processing pipelines.
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.
IT Architects Alliance
Discussion and exchange on system, internet, large‑scale distributed, high‑availability, and high‑performance architectures, as well as big data, machine learning, AI, and architecture adjustments with internet technologies. Includes real‑world large‑scale architecture case studies. Open to architects who have ideas and enjoy sharing.
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.
