Fundamentals 11 min read

Understanding the Chain of Responsibility Design Pattern with Java Examples

This article explains the Chain of Responsibility design pattern, its purpose and typical use cases, demonstrates a problematic nested‑if implementation, and then shows step‑by‑step refactoring into a clean, extensible handler chain using abstract classes, concrete handlers, and a factory in Java.

Architecture Digest
Architecture Digest
Architecture Digest
Understanding the Chain of Responsibility Design Pattern with Java Examples

Background : The author attempted to implement an import feature using the Chain of Responsibility pattern, which resulted in overly complex and buggy code, and notes that the Template Method would be a better fit.

What is Chain of Responsibility : It is a behavioral design pattern that allows a request to travel along a chain of handlers, where each handler can either process the request or pass it to the next handler.

Typical usage scenarios : multi‑condition flow control (e.g., permission checks), ERP approval processes (e.g., manager → HR → director), and the underlying implementation of Java servlet filters.

Anti‑example : A simple game with three levels is implemented using nested if statements, leading to duplicated logic and poor maintainability when the number of levels grows.

Initial implementation : Concrete handler classes ( FirstPassHandler, SecondPassHandler, ThirdPassHandler) each contain a handler() method and the client manually creates and links them.

Refactored version – basic chain : Introduce an abstract handler that holds a reference to the next handler, reducing client code and improving extensibility.

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 delegate to the next handler only when appropriate.

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,这是最后一关啦");
        return play();
    }
}

Client code now only creates the first handler and links the chain:

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();
    }
}

Further improvement – factory configuration : Use an enum ( GatewayEnum) and a DAO to describe each handler (id, name, class name, predecessor, successor). A static factory builds the chain dynamically 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));
    // ... constructor and getter omitted for brevity
}
public class GatewayHandlerEnumFactory {
    private static GatewayDao gatewayDao = new GatewayImpl();
    public static GatewayHandler getFirstGatewayHandler() {
        GatewayEntity firstEntity = gatewayDao.getFirstGatewayEntity();
        GatewayHandler firstHandler = newGatewayHandler(firstEntity);
        // iterate and link subsequent handlers
        // ... omitted for brevity
        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;
    }
}

The article concludes that design patterns like Chain of Responsibility are powerful tools that, when used appropriately, lead to cleaner, more maintainable code.

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.

Architecture Digest
Written by

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.

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.