Understanding the Chain of Responsibility Design Pattern with Java Examples
This article introduces the Chain of Responsibility design pattern, explains its purpose and typical use cases, demonstrates common pitfalls with a naïve implementation, and provides step‑by‑step Java refactorings—including a basic chain, an abstract handler hierarchy, and a factory‑based configuration—to illustrate how to build flexible, maintainable processing pipelines.
Recently a team member implemented an import feature using the Chain of Responsibility pattern, resulting in overly complex code and many bugs; the author argues that the Template Method pattern would have been more appropriate and uses this as a motivation to explore the Chain of Responsibility.
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, where 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.
Anti‑example
A simple game with three levels is implemented using nested if statements, leading to repetitive and hard‑to‑maintain code. The article shows how this approach quickly becomes unmanageable as 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();
SecondPassHandler secondPassHandler = new SecondPassHandler();
ThirdPassHandler thirdPassHandler = new ThirdPassHandler();
int firstScore = firstPassHandler.handler();
if(firstScore >= 80){
int secondScore = secondPassHandler.handler();
if(secondScore >= 90){
thirdPassHandler.handler();
}
}
}
}To avoid such duplication, the article proposes linking each level via a chain, so the client no longer needs multiple if checks.
public class FirstPassHandler extends AbstractHandler {
private int play(){ return 80; }
@Override
public int handler(){
System.out.println("第一关-->FirstPassHandler");
int score = play();
if(score >= 80 && this.next != null){
return this.next.handler();
}
return score;
}
}
// Similar implementations for SecondPassHandler and ThirdPassHandler
public abstract class AbstractHandler {
protected AbstractHandler next;
public void setNext(AbstractHandler next){ this.next = next; }
public abstract int handler();
}
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();
}
}Drawbacks of the Initial Implementation
Each level holds a reference to a different concrete next‑level class, making the chain hard to extend.
The code is not easily scalable; adding or removing levels requires substantial changes.
Factory‑Based Refactoring
The article further demonstrates how to externalize the chain configuration into an enum and a factory, allowing dynamic assembly of handlers via reflection.
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));
// ... getters and constructor ...
}
// Factory that builds the chain using the enum configuration
public class GatewayHandlerEnumFactory {
private static GatewayDao gatewayDao = new GatewayImpl();
public static GatewayHandler getFirstGatewayHandler(){
GatewayEntity firstEntity = gatewayDao.getFirstGatewayEntity();
GatewayHandler firstHandler = newGatewayHandler(firstEntity);
// link subsequent handlers based on nextHandlerId
// ...
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
Design patterns like the Chain of Responsibility provide a structured way to handle flexible processing flows; mastering them is essential for writing clean, maintainable code.
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.
Selected Java Interview Questions
A professional Java tech channel sharing common knowledge to help developers fill gaps. Follow us!
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.
