Fundamentals 10 min read

Mastering the Chain of Responsibility Pattern: Clean Task Delegation & Decoupling

This article explains the Chain of Responsibility design pattern, its core concepts, typical use‑cases such as approval workflows and logging, provides step‑by‑step Java examples—including a basic leave‑request system and an advanced builder‑based chain—covers real‑world framework integrations, compares it with other patterns, and lists practical pros, cons, and best‑practice tips.

Ray's Galactic Tech
Ray's Galactic Tech
Ray's Galactic Tech
Mastering the Chain of Responsibility Pattern: Clean Task Delegation & Decoupling

What is the Chain of Responsibility?

The Chain of Responsibility pattern decouples the sender of a request from its receivers, allowing multiple handlers to process the request in order and decide whether to pass it along.

Core Participants

Handler (interface) : declares the method to handle a request and to set the next handler.

ConcreteHandler : decides whether it can process the request; if not, it forwards the request to the next handler.

Client : builds the chain and initiates the request.

Typical Scenarios

Common applications include approval processes (leave, reimbursement, release), logging systems (DEBUG → INFO → WARN → ERROR), web filter chains (Spring Filter, Interceptor, Gateway Filter), exception handling (multi‑level try‑catch), and game event handling (click or key events).

Basic Java Example – Leave Approval

class LeaveRequest {
    private String employeeName;
    private int leaveDays;
    private String reason;
    // constructor, getters omitted for brevity
}

abstract class Approver {
    protected Approver nextApprover;
    protected String name;
    public Approver(String name) { this.name = name; }
    public Approver setNextApprover(Approver next) { this.nextApprover = next; return next; }
    public abstract void processRequest(LeaveRequest request);
}

class GroupLeader extends Approver {
    public GroupLeader(String name) { super(name); }
    @Override
    public void processRequest(LeaveRequest request) {
        if (request.getLeaveDays() <= 2) {
            System.out.println("组长" + name + "审批通过:" + request.getEmployeeName() + "的" + request.getLeaveDays() + "天请假");
        } else if (nextApprover != null) {
            System.out.println("组长" + name + "无权限审批,转交上级");
            nextApprover.processRequest(request);
        } else {
            System.out.println("无人能处理该请求");
        }
    }
}

class Manager extends Approver { /* similar, handles up to 5 days */ }
class Director extends Approver { /* similar, handles up to 10 days, otherwise rejects */ }

public class ChainOfResponsibilityDemo {
    public static void main(String[] args) {
        Approver groupLeader = new GroupLeader("张组长");
        Approver manager = new Manager("李经理");
        Approver director = new Director("王总监");
        groupLeader.setNextApprover(manager).setNextApprover(director);
        // test different leave requests
        groupLeader.processRequest(new LeaveRequest("张三", 1, "病假"));
        groupLeader.processRequest(new LeaveRequest("李四", 3, "事假"));
        groupLeader.processRequest(new LeaveRequest("王五", 7, "年假"));
        groupLeader.processRequest(new LeaveRequest("赵六", 15, "旅游"));
    }
}

Advanced Implementation – Flexible Handlers & Builder

interface EnhancedApprover {
    boolean canHandle(LeaveRequest request);
    void handle(LeaveRequest request);
    void setNext(EnhancedApprover next);
}

abstract class AbstractApprover implements EnhancedApprover {
    protected EnhancedApprover next;
    @Override public void setNext(EnhancedApprover next) { this.next = next; }
    @Override public void handle(LeaveRequest request) {
        if (canHandle(request)) { processRequest(request); }
        else if (next != null) { next.handle(request); }
        else { System.out.println("无人能处理该请求:" + request.getEmployeeName()); }
    }
    protected abstract void processRequest(LeaveRequest request);
}

class EmergencyApprover extends AbstractApprover {
    private String name;
    public EmergencyApprover(String name) { this.name = name; }
    @Override public boolean canHandle(LeaveRequest r) { return "紧急".equals(r.getReason()) || "病危".equals(r.getReason()); }
    @Override protected void processRequest(LeaveRequest r) { System.out.println("紧急处理员" + name + "特批:" + r.getEmployeeName() + "的" + r.getLeaveDays() + "天请假"); }
}

class ApprovalChainBuilder {
    private EnhancedApprover head, tail;
    public ApprovalChainBuilder addApprover(EnhancedApprover a) {
        if (head == null) { head = tail = a; }
        else { tail.setNext(a); tail = a; }
        return this;
    }
    public EnhancedApprover build() { return head; }
}

public class EnhancedChainDemo {
    public static void main(String[] args) {
        EnhancedApprover chain = new ApprovalChainBuilder()
            .addApprover(new EmergencyApprover("应急专员"))
            .addApprover(new GroupLeader("张组长"))
            .addApprover(new Manager("李经理"))
            .addApprover(new Director("王总监"))
            .build();
        chain.handle(new LeaveRequest("张三", 3, "事假"));
        chain.handle(new LeaveRequest("李四", 5, "紧急"));
        chain.handle(new LeaveRequest("王五", 20, "长期学习"));
    }
}

Real‑World Framework Usage

In Spring MVC, interceptors form a chain where each preHandle method returns true to continue. Servlet filters are linked via filterChain.doFilter. Netty’s ChannelPipeline adds handlers like LoggingHandler, AuthHandler, and BusinessHandler in order.

UML Class Diagram (Simplified)

The diagram shows Handler with a next reference, and concrete subclasses (e.g., ConcreteHandlerA, ConcreteHandlerB) extending it.

Comparison with Other Patterns

Observer : broadcasts to all observers; Chain of Responsibility passes sequentially and can stop propagation.

Strategy : selects one algorithm; Chain can involve multiple handlers.

Decorator : adds behavior layer‑by‑layer without affecting the core flow; Chain focuses on request routing.

Pros and Cons

Advantages : decouples sender and receiver, flexible ordering, adheres to the Open‑Closed Principle, mirrors real‑world processes.

Disadvantages : long chains may impact performance, lack of a default handler can cause lost requests, debugging can be difficult.

Best Practices & Pitfalls

Provide a default handler to avoid unprocessed requests.

Log or monitor long chains for performance insight.

Leverage Spring IoC with @Order to auto‑wire the chain.

In high‑concurrency or distributed systems, consider an asynchronous chain using message queues or event‑driven mechanisms.

Conclusion

The Chain of Responsibility pattern offers high extensibility, low coupling, and elegant task distribution, making it a powerful tool for building maintainable and flexible software architectures.

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 patternDecoupling
Ray's Galactic Tech
Written by

Ray's Galactic Tech

Practice together, never alone. We cover programming languages, development tools, learning methods, and pitfall notes. We simplify complex topics, guiding you from beginner to advanced. Weekly practical content—let's grow together!

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.