责任链模式

180 阅读4分钟

点击阅读:设计模式系列文章


1. 责任链模式的定义

责任链模式(Chain of Responsibility Pattern) 是一种行为型设计模式,它的核心思想是: 将请求的发送者与处理者解耦,使多个对象都有机会处理请求,并将请求沿链传递,直到被处理。

  1. 解耦请求发送者与处理,发送者可以不需要关心谁最终处理请求。
  2. 支持动态组合和顺序变更。
  3. 链上的节点可以自由决定是否处理请求。每个处理者可以决定:自己处理或传给下一个。

2. 责任链模式的几种模型:

2.1 代码结构层面分类

2.1.1 链表式结构

去中心化,简单的业务链,或者节点之间有强顺序依赖。

[Client]
   |
   v
+-----------+        +-----------+        +-----------+
| Handler A | -----> | Handler B | -----> | Handler C | -----> null
+-----------+        +-----------+        +-----------+
| next: B   |        | next: C   |        | next: null|
+-----------+        +-----------+        +-----------+

2.1.2 集中管理器结构

Chain 管理器中心化管理,节点间解耦,容易动态排序、插入、删除。

[Client]
   |
   v
+-----------------------------+
|     Chain Manager           | (持有 List<Handler>)
| --------------------------- |
| index = 0                   |
|                             |
|  1. [Handler A] <---------+ | (Manager 循环调用或递归调用)
|  2. [Handler B] <---------+ |
|  3. [Handler C] <---------+ |
+-----------------------------+

2.2 业务逻辑层面分类

2.2.1 短路型

短路型 (Short-circuit),链上任何一个节点处理完请求后,直接中断,不再向下传递。适用于“权限校验”、“参数验证”等场景,一旦发现问题就无需继续。

Request
        |
        v
+----------------+      NO
|  1. AuthCheck  | ----------------> [ 拒绝/报错 ]
+----------------+
        | YES (继续)
        v
+----------------+      NO
|  2. ParamCheck | ----------------> [ 拒绝/报错 ]
+----------------+
        | YES (继续)
        v
+----------------+
|  3. Business   | (只有前两关都过,才能到这)
+----------------+

2.2.1 贯穿型

贯穿型 (Pass-through):链上所有节点都会处理请求,每个节点处理完后都会传递给下一个。适用于“日志记录”、“性能监控”、“数据审计”等需要多重处理的场景。

Request
        |
        v
+----------------+
|  1. LogHandler | (记录:"请求进来了")
+----------------+
        |
        v
+----------------+
|  2. FormatHdlr | (动作:把参数去空格、大写化)
+----------------+
        |
        v
+----------------+
|  3. AuditHdlr  | (记录:"数据被修改了")
+----------------+
        |
        v
     [最终处理]

3. 责任链模式代码举例

Client
   |
Handler (抽象)
   |
ConcreteHandler1 --> ConcreteHandler2 --> ConcreteHandler3 ...

3.1 Handler 抽象处理者

public abstract class Handler {
    protected Handler next;

    public Handler setNext(Handler next) {
        this.next = next;
        return next;
    }

    public final void handle(RequestContext ctx) {
        if (onHandle(ctx)) {
            return;
        }
        if (next != null) {
            next.handle(ctx);
        }
    }

    protected abstract boolean onHandle(RequestContext ctx);
}

3.2 ConcreteHandler 具体处理者

public class AuthHandler extends Handler {
    @Override
    protected boolean onHandle(RequestContext ctx) {
        String token = (String) ctx.get("token");
        if (token == null || token.isEmpty()) {
            throw new RuntimeException("unauthorized");
        }
        return false;
    }
}

public class ParamValidateHandler extends Handler {
    @Override
    protected boolean onHandle(RequestContext ctx) {
        Object uid = ctx.get("userId");
        if (uid == null) {
            throw new IllegalArgumentException("userId required");
        }
        return false;
    }
}

public class BizHandler extends Handler {
    @Override
    protected boolean onHandle(RequestContext ctx) {
        System.out.println("do real business");
        return true;
    }
}

3.3 Client 客户端

// 客户端调用
public class Client {
    public static void main(String[] args) {
        RequestContext ctx = new RequestContext();
        ctx.put("token", "abc");
        ctx.put("userId", 123);

        Handler head = new AuthHandler();
        head.setNext(new ParamValidateHandler())
            .setNext(new BizHandler());

        head.handle(ctx);
    }
}

public class RequestContext {
    private final Map<String, Object> attrs = new HashMap<>();

    public void put(String key, Object value) {
        attrs.put(key, value);
    }

    public Object get(String key) {
        return attrs.get(key);
    }
}

4. 责任链模式的设计思想

  • 解耦请求发送者与处理者

  • 单一职责:节点只处理自身逻辑。

  • 递归/循环链式传递,体现动态和灵活。

  • 开闭原则:新增节点无需改动原有链。

  • 可扩展性:适合多种复杂业务场景。

5. 一些问题

Q1: 责任链模式与装饰器模式的区别是什么?

A1:

  • 责任链模式:多个处理器按顺序尝试处理同一个请求,直到有一个处理器能够处理为止(或全部处理完)
  • 装饰器模式:对同一个对象进行层层包装,每层都增加新的功能
  • 核心区别:责任链强调"选择性处理",装饰器强调"功能叠加"
// 责任链:只有一个处理器会处理请求
handler1.handle(request) -> handler2.handle(request) -> handler3.handle(request)

// 装饰器:每层都会处理,功能叠加
decorator1(decorator2(decorator3(core)))

Q2: 责任链模式在什么场景下不适用?

  1. 处理逻辑简单且固定

    • 解释:如果处理步骤始终固定且不需要动态调整,那么直接用顺序调用或 if-else 就足够。
    • 例子:一个请求只需要固定地经过 A→B→C 三个处理器,且不会变更。此时用责任链反而显得多余、复杂。
  2. 性能要求极高

    • 解释:责任链需要依次遍历处理器,每次调用都涉及方法分发甚至对象查找,存在额外开销。
    • 例子:在 Android Framework 的核心渲染或输入事件分发等高频调用场景,如果链很长,会产生明显性能损耗。
  3. 处理器之间有强依赖关系

    • 解释:责任链强调 解耦(各处理器独立,不关心前后顺序细节)。但如果处理器必须依赖上一步的具体实现,责任链就不合适。
    • 例子:步骤 B 必须依赖步骤 A 的内部状态或返回值,而不仅仅是一个统一的上下文参数,这种强依赖违背了解耦设计。
  4. 需要回滚操作

    • 解释:责任链是单向流转,请求被传递下去后通常不追溯处理历史。如果业务需要类似事务的回滚(所有步骤要么全部成功,要么全部撤销),责任链难以实现。
    • 例子:在数据库批处理或支付流程中,某个处理失败时需要撤销之前已完成的步骤;责任链无法天然支持,需要额外补偿机制。