Chain of Responsibility Design Pattern: Concepts, Drawbacks, and Refactoring in Java
This article explains the Chain of Responsibility design pattern, its use cases, and demonstrates step‑by‑step refactoring in Java—from a verbose conditional example to a clean, extensible handler chain, including abstract handlers, concrete handlers, and a factory‑based configuration.
Background
The author describes a situation where 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 pattern would be more appropriate and uses this example to introduce the Chain of Responsibility pattern.
What is 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 Use Cases
Multi‑condition flow control such as permission checks.
ERP workflow approvals (e.g., manager → HR → project lead).
Implementation of Java servlet filters.
Anti‑Pattern Example
A naïve implementation for a three‑level game uses nested if statements, leading to repetitive and hard‑to‑maintain code. The source code is shown below:
// First level
public class FirstPassHandler {
public int handler() {
System.out.println("第一关-->FirstPassHandler");
return 80;
}
}
// Second level
public class SecondPassHandler {
public int handler() {
System.out.println("第二关-->SecondPassHandler");
return 90;
}
}
// Third level
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();
}
}
}
}When the number of levels grows (e.g., 100 levels), the code becomes a deep nesting of if statements, which is difficult to extend and error‑prone.
Initial Refactoring Using Explicit Links
To avoid deep nesting, each handler holds a reference to the next handler, and the client only invokes the first handler. The refactored code looks like this:
public class FirstPassHandler {
private SecondPassHandler secondPassHandler;
public void setSecondPassHandler(SecondPassHandler h) { this.secondPassHandler = h; }
private int play() { return 80; }
public int handler() {
System.out.println("第一关-->FirstPassHandler");
if (play() >= 80 && this.secondPassHandler != null) {
return this.secondPassHandler.handler();
}
return 80;
}
}
public class SecondPassHandler {
private ThirdPassHandler thirdPassHandler;
public void setThirdPassHandler(ThirdPassHandler h) { this.thirdPassHandler = h; }
private int play() { return 90; }
public int handler() {
System.out.println("第二关-->SecondPassHandler");
if (play() >= 90 && this.thirdPassHandler != null) {
return this.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();
}
}This version eliminates the deep if nesting, but each concrete handler still declares a field for its specific successor, which reduces extensibility.
Further Refactoring with an Abstract Handler
To make the chain truly generic, an abstract base class defines a next reference of the same abstract type. Concrete handlers extend this class and only implement their own processing logic.
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 && this.next != null) {
return this.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 && this.next != null) {
return this.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 && this.next != null) {
return this.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();
}
}Now each handler only knows about the abstract next handler, improving reusability and making the chain easy to extend.
Factory‑Based Dynamic Chain Construction
The article further shows how to configure the chain via an enum and a factory that uses reflection to instantiate handlers based on fully‑qualified class names. This allows the chain structure to be defined in configuration rather than hard‑coded.
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 GatewayEntity gatewayEntity;
GatewayEnum(GatewayEntity e) { this.gatewayEntity = e; }
public GatewayEntity getGatewayEntity() { return gatewayEntity; }
}
public class GatewayHandlerEnumFactory {
private static 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.newInstance();
} catch (Exception e) { e.printStackTrace(); }
return null;
}
}This approach decouples the chain definition from the code, enabling dynamic reconfiguration without recompilation.
Conclusion
Design patterns like Chain of Responsibility help keep code maintainable and extensible. By moving from a tangled conditional structure to a clean handler chain—and finally to a configuration‑driven factory—you can dramatically improve readability, reduce bugs, and simplify future changes.
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.
Architecture Digest
Focusing on Java backend development, covering application architecture from top-tier internet companies (high availability, high performance, high stability), big data, machine learning, Java architecture, and other popular fields.
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.
