Skip to content

责任链模式(Chain of Responsibility Pattern) ​

一、模式概述 ​

责任链模式是一种行为型设计模式,它为请求创建了一个处理者对象的链。每个处理者包含对下一个处理者的引用,请求沿着链传递,直到某个处理者处理它为止。

核心思想:将请求的发送者和接收者解耦,让多个对象都有机会处理这个请求。将这些对象连成一条链,并沿着这条链传递请求,直到有一个对象处理它为止。

二、角色说明 ​

角色说明
Handler(抽象处理者)定义处理请求的接口,包含对下一个处理者的引用
ConcreteHandler(具体处理者)实现处理请求的逻辑,决定是处理还是传递给下一个处理者
Client(客户端)创建处理者链,发送请求

三、生活中的例子 ​

  • 审批流程:员工请假 -> 组长审批 -> 经理审批 -> 总监审批 -> CEO审批
  • Servlet Filter:请求经过多个过滤器(编码、认证、日志等)
  • 异常处理:异常沿着调用栈向上传播,直到被某个 catch 捕获
  • DOM 事件冒泡:点击事件从最内层元素向外层传播

四、示例1:审批工作流系统 ​

问题场景 ​

公司请假审批流程:不同天数的请假需要不同级别的领导审批。

java
// ========== 抽象处理者 ==========

/**
 * 审批处理者(抽象类)
 * 定义了审批链的结构和通用逻辑
 */
abstract class ApprovalHandler {
    protected ApprovalHandler nextHandler;  // 下一个处理者
    protected String name;
    protected String position;
    protected int maxLeaveDays;  // 最大可审批天数

    public ApprovalHandler(String name, String position, int maxLeaveDays) {
        this.name = name;
        this.position = position;
        this.maxLeaveDays = maxLeaveDays;
    }

    /**
     * 设置下一个处理者(支持链式调用)
     */
    public ApprovalHandler setNext(ApprovalHandler next) {
        this.nextHandler = next;
        return next;  // 返回下一个处理者,方便链式设置
    }

    /**
     * 处理审批请求
     */
    public final void handleRequest(LeaveRequest request) {
        if (canHandle(request)) {
            process(request);
        } else {
            System.out.println("[" + position + " " + name + "] "
                    + "无权审批 " + request.getDays() + " 天请假,转交上级...");
            if (nextHandler != null) {
                nextHandler.handleRequest(request);
            } else {
                System.out.println("[系统] 没有更高权限的审批人,申请被拒绝!");
            }
        }
    }

    /**
     * 判断是否有权限处理该请求
     */
    protected boolean canHandle(LeaveRequest request) {
        return request.getDays() <= maxLeaveDays;
    }

    /**
     * 具体的审批逻辑(由子类实现)
     */
    protected abstract void process(LeaveRequest request);
}

// ========== 具体处理者 ==========

/**
 * 组长:最多可审批3天
 */
class TeamLeader extends ApprovalHandler {
    public TeamLeader(String name) {
        super(name, "组长", 3);
    }

    @Override
    protected void process(LeaveRequest request) {
        System.out.println("[组长 " + name + "] 审批通过!"
                + " " + request.getEmployee() + " 请假 " + request.getDays()
                + " 天,原因: " + request.getReason());
    }
}

/**
 * 经理:最多可审批7天
 */
class Manager extends ApprovalHandler {
    public Manager(String name) {
        super(name, "经理", 7);
    }

    @Override
    protected void process(LeaveRequest request) {
        if (request.getDays() > 3) {
            System.out.println("[经理 " + name + "] 审批通过!"
                    + " " + request.getEmployee() + " 请假 " + request.getDays()
                    + " 天,原因: " + request.getReason());
        } else {
            // 经理也可以处理3天以下的,但通常不处理
            System.out.println("[经理 " + name + "] 审批通过!"
                    + " " + request.getEmployee() + " 请假 " + request.getDays()
                    + " 天,原因: " + request.getReason());
        }
    }
}

/**
 * 总监:最多可审批15天
 */
class Director extends ApprovalHandler {
    public Director(String name) {
        super(name, "总监", 15);
    }

    @Override
    protected void process(LeaveRequest request) {
        System.out.println("[总监 " + name + "] 审批通过!"
                + " " + request.getEmployee() + " 请假 " + request.getDays()
                + " 天,原因: " + request.getReason());
    }
}

/**
 * CEO:无限制
 */
class CEO extends ApprovalHandler {
    public CEO(String name) {
        super(name, "CEO", Integer.MAX_VALUE);
    }

    @Override
    protected void process(LeaveRequest request) {
        System.out.println("[CEO " + name + "] 审批通过!"
                + " " + request.getEmployee() + " 请假 " + request.getDays()
                + " 天,原因: " + request.getReason());
    }
}

// ========== 请假请求 ==========

/**
 * 请假请求
 */
class LeaveRequest {
    private String employee;  // 请假人
    private int days;         // 请假天数
    private String reason;    // 请假原因

    public LeaveRequest(String employee, int days, String reason) {
        this.employee = employee;
        this.days = days;
        this.reason = reason;
    }

    public String getEmployee() { return employee; }
    public int getDays() { return days; }
    public String getReason() { return reason; }
}

// ========== 客户端代码 ==========

public class ApprovalChainDemo {
    public static void main(String[] args) {
        // 创建审批链:组长 -> 经理 -> 总监 -> CEO
        ApprovalHandler teamLeader = new TeamLeader("王组长");
        ApprovalHandler manager    = new Manager("李经理");
        ApprovalHandler director   = new Director("张总监");
        ApprovalHandler ceo        = new CEO("刘CEO");

        teamLeader.setNext(manager)
                  .setNext(director)
                  .setNext(ceo);

        System.out.println("===== 请假2天 =====");
        teamLeader.handleRequest(new LeaveRequest("小明", 2, "感冒发烧"));

        System.out.println("\n===== 请假5天 =====");
        teamLeader.handleRequest(new LeaveRequest("小红", 5, "回家探亲"));

        System.out.println("\n===== 请假10天 =====");
        teamLeader.handleRequest(new LeaveRequest("小刚", 10, "出国旅游"));

        System.out.println("\n===== 请假30天 =====");
        teamLeader.handleRequest(new LeaveRequest("小李", 30, "结婚"));
    }
}

运行结果 ​

===== 请假2天 =====
[组长 王组长] 审批通过! 小明 请假 2 天,原因: 感冒发烧

===== 请假5天 =====
[组长 王组长] 无权审批 5 天请假,转交上级...
[经理 李经理] 审批通过! 小红 请假 5 天,原因: 回家探亲

===== 请假10天 =====
[组长 王组长] 无权审批 10 天请假,转交上级...
[经理 李经理] 无权审批 10 天请假,转交上级...
[总监 张总监] 审批通过! 小刚 请假 10 天,原因: 出国旅游

===== 请假30天 =====
[组长 王组长] 无权审批 30 天请假,转交上级...
[经理 李经理] 无权审批 30 天请假,转交上级...
[总监 张总监] 无权审批 30 天请假,转交上级...
[CEO 刘CEO] 审批通过! 小李 请假 30 天,原因: 结婚

五、示例2:Servlet Filter 链 ​

模拟 Servlet Filter 的工作原理 ​

java
import java.util.ArrayList;
import java.util.List;

// ========== Filter 接口 ==========

/**
 * 模拟 Servlet Filter 接口
 */
interface WebFilter {
    String getName();
    void doFilter(FilterRequest request, FilterChain chain);
}

/**
 * 过滤器链
 */
class FilterChain {
    private final List<WebFilter> filters = new ArrayList<>();
    private int index = 0;

    public FilterChain addFilter(WebFilter filter) {
        filters.add(filter);
        return this;
    }

    /**
     * 执行过滤器链
     */
    public void doFilter(FilterRequest request) {
        if (index < filters.size()) {
            WebFilter filter = filters.get(index++);
            System.out.println("  [Filter] 进入: " + filter.getName());
            filter.doFilter(request, this);
            System.out.println("  [Filter] 退出: " + filter.getName());
        } else {
            System.out.println("  [Filter] 所有过滤器执行完毕,处理请求...");
        }
    }
}

/**
 * 过滤器请求
 */
class FilterRequest {
    private String url;
    private String method;
    private String contentType;
    private String authToken;
    private java.util.Map<String, String> headers = new java.util.HashMap<>();

    public FilterRequest(String url, String method) {
        this.url = url;
        this.method = method;
    }

    public String getUrl() { return url; }
    public String getMethod() { return method; }
    public String getContentType() { return contentType; }
    public void setContentType(String type) { this.contentType = type; }
    public String getAuthToken() { return authToken; }
    public void setAuthToken(String token) { this.authToken = token; }
    public java.util.Map<String, String> getHeaders() { return headers; }
}

// ========== 具体过滤器 ==========

/**
 * 编码过滤器:设置字符编码
 */
class EncodingFilter implements WebFilter {
    @Override
    public String getName() { return "EncodingFilter"; }

    @Override
    public void doFilter(FilterRequest request, FilterChain chain) {
        System.out.println("    [编码过滤器] 设置 UTF-8 编码");
        request.setContentType("text/html;charset=UTF-8");
        chain.doFilter(request);  // 继续下一个过滤器
    }
}

/**
 * 日志过滤器:记录请求日志
 */
class LoggingFilter implements WebFilter {
    @Override
    public String getName() { return "LoggingFilter"; }

    @Override
    public void doFilter(FilterRequest request, FilterChain chain) {
        long start = System.currentTimeMillis();
        System.out.println("    [日志过滤器] 请求: " + request.getMethod()
                + " " + request.getUrl());
        chain.doFilter(request);  // 继续下一个过滤器
        long cost = System.currentTimeMillis() - start;
        System.out.println("    [日志过滤器] 响应时间: " + cost + "ms");
    }
}

/**
 * 认证过滤器:检查登录状态
 */
class AuthFilter implements WebFilter {
    @Override
    public String getName() { return "AuthFilter"; }

    @Override
    public void doFilter(FilterRequest request, FilterChain chain) {
        String token = request.getAuthToken();
        if (token == null || token.isEmpty()) {
            System.out.println("    [认证过滤器] 未登录,拒绝访问!");
            return;  // 不调用 chain.doFilter,请求到此为止
        }
        System.out.println("    [认证过滤器] Token验证通过");
        chain.doFilter(request);  // 继续下一个过滤器
    }
}

/**
 * CORS 过滤器:处理跨域请求
 */
class CorsFilter implements WebFilter {
    @Override
    public String getName() { return "CorsFilter"; }

    @Override
    public void doFilter(FilterRequest request, FilterChain chain) {
        System.out.println("    [CORS过滤器] 添加跨域头: Access-Control-Allow-Origin: *");
        request.getHeaders().put("Access-Control-Allow-Origin", "*");
        chain.doFilter(request);
    }
}

// ========== 客户端代码 ==========

public class FilterChainDemo {
    public static void main(String[] args) {
        // 构建过滤器链
        FilterChain chain = new FilterChain();
        chain.addFilter(new LoggingFilter())
             .addFilter(new EncodingFilter())
             .addFilter(new AuthFilter())
             .addFilter(new CorsFilter());

        // 模拟请求1:已登录
        System.out.println("===== 请求1:已登录用户访问 /api/users =====");
        FilterRequest request1 = new FilterRequest("/api/users", "GET");
        request1.setAuthToken("abc123");
        chain.doFilter(request1);

        System.out.println();

        // 模拟请求2:未登录
        System.out.println("===== 请求2:未登录用户访问 /api/orders =====");
        FilterChain chain2 = new FilterChain();  // 需要新建链(index已重置)
        chain2.addFilter(new LoggingFilter())
              .addFilter(new EncodingFilter())
              .addFilter(new AuthFilter())
              .addFilter(new CorsFilter());
        FilterRequest request2 = new FilterRequest("/api/orders", "POST");
        chain2.doFilter(request2);
    }
}

六、示例3:支持动态添加处理器的责任链 ​

java
import java.util.ArrayList;
import java.util.List;
import java.util.function.Predicate;

/**
 * 可动态构建的责任链(使用函数式接口)
 */
class DynamicChain<T> {
    private final List<ChainHandler<T>> handlers = new ArrayList<>();

    /**
     * 添加处理器
     */
    public DynamicChain<T> addHandler(Predicate<T> condition, java.util.function.Consumer<T> action) {
        handlers.add(new ChainHandler<>(condition, action));
        return this;
    }

    /**
     * 执行责任链
     * 如果某个处理器处理了请求(返回true),则停止传递
     */
    public void process(T request) {
        for (ChainHandler<T> handler : handlers) {
            if (handler.condition.test(request)) {
                handler.action.accept(request);
                return;  // 已处理,停止传递
            }
        }
        System.out.println("  [系统] 没有处理器能处理此请求");
    }

    private static class ChainHandler<T> {
        Predicate<T> condition;
        java.util.function.Consumer<T> action;

        ChainHandler(Predicate<T> condition, java.util.function.Consumer<T> action) {
            this.condition = condition;
            this.action = action;
        }
    }
}

/**
 * 支付请求
 */
class PaymentRequest {
    double amount;
    String type;  // "alipay", "wechat", "bank"

    public PaymentRequest(double amount, String type) {
        this.amount = amount;
        this.type = type;
    }
}

// ========== 客户端代码 ==========

public class DynamicChainDemo {
    public static void main(String[] args) {
        DynamicChain<PaymentRequest> paymentChain = new DynamicChain<>();
        paymentChain
            .addHandler(r -> r.type.equals("alipay") && r.amount <= 5000,
                r -> System.out.println("  [支付宝] 处理 " + r.amount + " 元支付"))
            .addHandler(r -> r.type.equals("alipay"),
                r -> System.out.println("  [支付宝-大额] 处理 " + r.amount + " 元大额支付"))
            .addHandler(r -> r.type.equals("wechat"),
                r -> System.out.println("  [微信支付] 处理 " + r.amount + " 元支付"))
            .addHandler(r -> r.type.equals("bank"),
                r -> System.out.println("  [银行转账] 处理 " + r.amount + " 元支付"));

        System.out.println("===== 支付宝小额支付 =====");
        paymentChain.process(new PaymentRequest(100, "alipay"));

        System.out.println("\n===== 支付宝大额支付 =====");
        paymentChain.process(new PaymentRequest(10000, "alipay"));

        System.out.println("\n===== 微信支付 =====");
        paymentChain.process(new PaymentRequest(200, "wechat"));

        System.out.println("\n===== 银行转账 =====");
        paymentChain.process(new PaymentRequest(50000, "bank"));
    }
}

七、UML 类图 ​

┌──────────────┐
│    Client     │───创建──▶ Handler Chain
└──────────────┘
                  │
                  ▼
┌──────────────────────────┐
│   Handler (抽象处理者)     │
│   - nextHandler: Handler │
│   + handleRequest()      │
└──────────┬───────────────┘
           │ 继承
    ┌──────┴──────┐
    │             │
┌───┴────┐  ┌───┴────┐  ┌──────────┐
│Concrete│  │Concrete│  │Concrete  │
│Handler1│  │Handler2│  │Handler3  │
│process()│  │process()│  │process()│
└────────┘  └────────┘  └──────────┘
    │             │            │
    └────next─────┘────next────┘

八、优缺点分析 ​

优点 ​

  1. 解耦:发送者不需要知道请求由哪个处理者处理
  • 单一职责:每个处理者只关注自己的处理逻辑
  • 灵活性强:可以动态调整处理者的顺序或增删处理者
  • 符合开闭原则:新增处理者不需要修改现有代码

缺点 ​

  1. 不能保证一定被处理:如果链上没有处理者能处理,请求可能丢失
  2. 链过长影响性能:请求可能需要经过很多处理者才能被处理
  3. 调试困难:请求在链中传递,调试时不容易跟踪
  4. 循环引用:如果链设置不当,可能造成无限循环

九、使用场景 ​

  • Web 框架:Servlet Filter、Spring Interceptor、Netty ChannelPipeline
  • 审批流程:OA系统中的多级审批
  • 异常处理:异常沿着调用栈传播
  • 中间件:日志、认证、限流等中间件
  • 事件处理:GUI事件的冒泡机制
  • Java 异常处理:try-catch-finally 链

十、最佳实践 ​

  1. 确保链有终止条件:避免请求永远不被处理
  2. 控制链的长度:过长的链会影响性能和可维护性
  3. 考虑使用责任链+组合模式:处理树形结构的请求
  4. 注意性能:每个处理者都可能执行,考虑缓存机制
  5. 合理设置处理顺序:通用的处理者放在前面,特殊的放在后面

总结:责任链模式将请求的发送者和接收者解耦,让多个对象都有机会处理请求。在实际应用中,Servlet Filter、Spring Interceptor、审批流程等都是责任链模式的典型应用。使用时要注意确保链有终止条件,避免请求丢失或无限循环。