Fundamentals 12 min read

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.

Java Backend Technology
Java Backend Technology
Java Backend Technology
Mastering the Chain of Responsibility Pattern: From Pitfalls to Clean Implementations

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.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

Chain of ResponsibilitySoftware Architecturedesign patternrefactoring
Java Backend Technology
Written by

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!

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.