Mastering the Chain of Responsibility Pattern: From Pitfalls to Clean Implementations
This article explains the Chain of Responsibility design pattern, illustrates common misuse with a multi‑if game‑level example, and then shows step‑by‑step refactorings—including linked handlers, an abstract base class, a factory and enum‑based configuration—to produce clean, extensible Java code.
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 process the request or forward it to the next handler.
Typical Scenarios
Multi‑condition flow control such as permission checks
ERP approval processes (e.g., manager → HR manager → project manager)
Underlying implementation of Java servlet filters
Anti‑Example
A simple game with three levels where each level checks the previous score using nested if statements:
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 become a deep cascade of if statements, making maintenance painful.
if (第1关通过) { if (第2关通过) { if (第3关通过) { /* … */ } } }Initial Refactor
Connect each level via a linked list so the client only invokes the first handler:
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"); int score = play(); if (score >= 80 && secondPassHandler != null) { return secondPassHandler.handler(); } return score; } }Similar changes are made to SecondPassHandler and ThirdPassHandler, and the client wires them together before calling firstPassHandler.handler().
Drawbacks of the Initial Refactor
Each concrete handler holds a different member variable for the next handler, making the chain hard to manage.
Extension is cumbersome because the concrete classes must be modified for every new handler.
Abstract Handler Refactor
Introduce an abstract base class that defines a protected next reference and a common setNext method.
public abstract class AbstractHandler { protected AbstractHandler next; public void setNext(AbstractHandler next) { this.next = next; } public abstract int handler(); }Concrete handlers now extend AbstractHandler and use the unified next field.
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; } }The client creates the handlers, links them via setNext, and starts the chain with firstPassHandler.handler().
Factory & Enum Configuration
To avoid hard‑coding the chain, an enum GatewayEnum stores metadata (handler ID, name, fully‑qualified class name, previous/next IDs). A GatewayDao implementation loads these entries into a map.
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; /* getters and constructor */ }A factory class uses reflection to instantiate each handler and links them according to the enum configuration:
public class GatewayHandlerEnumFactory { private static GatewayDao gatewayDao = new GatewayImpl(); public static GatewayHandler getFirstGatewayHandler() { GatewayEntity first = gatewayDao.getFirstGatewayEntity(); GatewayHandler firstHandler = newGatewayHandler(first); 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; } } }The client simply obtains the first handler from the factory and calls service(), leaving the chain configuration entirely external.
Other Thoughts
Design patterns are an art; the Chain of Responsibility is just one useful tool among many, and mastering it 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.
