Fundamentals 12 min read

Understanding the Chain of Responsibility Design Pattern with Real‑World Stories and Java Implementations

This article explains the Chain of Responsibility design pattern by linking a historical story of Guan Yu’s five passes with an OA leave‑approval workflow, provides a clear definition, discusses its pros and cons, and presents comprehensive Java code examples illustrating abstract handlers, concrete handlers, and client usage.

Full-Stack Internet Architecture
Full-Stack Internet Architecture
Full-Stack Internet Architecture
Understanding the Chain of Responsibility Design Pattern with Real‑World Stories and Java Implementations

The author introduces the Chain of Responsibility (CoR) pattern, using a vivid story from the classic novel "Romance of the Three Kingdoms" where Guan Yu passes five gates and slays six generals, then connects this narrative to a real‑world OA leave‑approval scenario.

The story is broken down into five gates: East Ridge (defeating Kong Xiu), Luoyang (defeating Meng Tan and Han Fu), Sishui (defeating Bian Xi), Yingyang (defeating Wang Zhi), and the Yellow River crossing (defeating Qin Qi). This sequential handling mirrors the CoR concept of passing a request along a chain until one handler processes it.

In the OA example, a leave request is routed through a project leader, a technical director, and finally the boss, depending on the number of leave days, illustrating the pattern in everyday business processes.

The pattern is defined in English as: "Avoid coupling the sender of a request to its receiver by giving more than one object a chance to handle the request. Chain the receiving objects and pass the request along the chain until an object handles it." It belongs to the behavioral design pattern category.

public abstract class Handler {
    protected Handler nextHandler = null;
    public abstract void handle();
    public Handler getNextHandler() { return nextHandler; }
    public void setNextHandler(Handler nextHandler) { this.nextHandler = nextHandler; }
}

public class HandlerA extends Handler {
    @Override
    public void handle() {
        if (nextHandler == null) {
            System.out.println("HandlerA handle ...");
        } else {
            nextHandler.handle();
        }
    }
}

public class HandlerB extends Handler {
    @Override
    public void handle() {
        if (nextHandler == null) {
            System.out.println("HandlerB handle ...");
        } else {
            nextHandler.handle();
        }
    }
}

public class HandlerC extends Handler {
    @Override
    public void handle() {
        if (getNextHandler() == null) {
            System.out.println("HandlerC handle ...");
        } else {
            getNextHandler().handle();
        }
    }
}

public class Client {
    public static void main(String[] args) {
        Handler handlerA = new HandlerA();
        Handler handlerB = new HandlerB();
        handlerA.setNextHandler(handlerB);
        handlerA.handle();
    }
}

The execution prints "HandlerC handle ...". A UML diagram (omitted here) shows the abstract Handler and its concrete subclasses, highlighting the two key roles: the abstract handler that defines the interface and holds a reference to the next handler, and the concrete handlers that either process the request or delegate it.

Advantages: decouples request senders from receivers and adds flexibility. Disadvantages: can degrade performance when the chain becomes very long.

For the leave‑approval workflow, the following Java classes implement the same pattern:

public abstract class Leader {
    private Leader next;
    public void setNext(Leader next) { this.next = next; }
    public Leader getNext() { return next; }
    public abstract void handleRequest(double leaveDays);
}

public class ProjectLeader extends Leader {
    @Override
    public void handleRequest(double leaveDays) {
        if (leaveDays <= 0.5) {
            System.out.println("项目负责人批准您请假" + leaveDays + "天。");
        } else if (getNext() != null) {
            getNext().handleRequest(leaveDays);
        } else {
            System.out.println("请假天数太多,没有人批准该假条!");
        }
    }
}

public class TechnicalDirectorLeader extends Leader {
    @Override
    public void handleRequest(double leaveDays) {
        if (leaveDays <= 1) {
            System.out.println("技术总监批准您请假" + leaveDays + "天。");
        } else if (getNext() != null) {
            getNext().handleRequest(leaveDays);
        } else {
            System.out.println("请假天数太多,没有人批准该假条!");
        }
    }
}

public class BossLeader extends Leader {
    @Override
    public void handleRequest(double leaveDays) {
        if (leaveDays >= 2 && leaveDays <= 30) {
            System.out.println("Boss批准您请假" + leaveDays + "天。");
        } else if (getNext() != null) {
            getNext().handleRequest(leaveDays);
        } else {
            System.out.println("请假天数太多,没有人批准该假条!");
        }
    }
}

public class LeaveApproval {
    public static void main(String[] args) {
        Leader projectLeader = new ProjectLeader();
        Leader technicalDirector = new TechnicalDirectorLeader();
        Leader boss = new BossLeader();
        projectLeader.setNext(technicalDirector);
        technicalDirector.setNext(boss);
        projectLeader.handleRequest(2); // Example: two days leave
    }
}

Running the program with a two‑day request prints "Boss批准您请假2.0天。"; a request of 31 days prints "请假天数太多,没有人批准该假条!".

The pattern also appears in major frameworks. In Spring MVC, the DispatcherServlet uses a chain of HandlerMapping objects in its getHandler method to locate a suitable handler for an incoming request:

@Nullable
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
    if (this.handlerMappings != null) {
        for (HandlerMapping mapping : this.handlerMappings) {
            HandlerExecutionChain handler = mapping.getHandler(request);
            if (handler != null) {
                return handler;
            }
        }
    }
    return null;
}

MyBatis also employs a similar chain of interceptors, as referenced in the linked article.

In summary, the article uses the Guan Yu legend and an OA leave‑approval flow to illustrate the Chain of Responsibility pattern, emphasizing that each participant handles its part and passes the request onward until it is resolved.

各人自扫门前雪,莫管他人瓦上霜。
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 ResponsibilityJavaSoftware Architecturedesign patternOO Design
Full-Stack Internet Architecture
Written by

Full-Stack Internet Architecture

Introducing full-stack Internet architecture technologies centered on Java

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.