基于本文回答
0
评论

设计模式--责任链模式 (Chain of Responsibility)

知识点图片

责任链模式将请求沿处理者链传递,解耦了发送者和接收者。处理者可选择处理或传递请求,使处理流程灵活可配。

责任链模式(Chain of Responsibility Pattern)是一种行为设计模式,它允许你将请求沿着处理者链进行发送。收到请求后,每个处理者均可对请求进行处理,或将其传递给链中的下一个处理者。

这种模式的核心思想是:为请求创建了一个接收者对象的链,从而避免请求的发送者和接收者之间的耦合。

意图 / 解决的问题

  1. 解耦发送者与接收者:请求的发送方不需要知道哪一个对象会最终处理这个请求。它只需将请求发送到链的第一个节点即可。
  2. 动态组合责任:可以动态地改变链中的成员或调动它们的次序,增加了处理请求的灵活性。
  3. 单一职责原则:每个处理者只需要关注自己的处理逻辑,而不需要知道链的整体结构。

核心角色

  1. Handler(抽象处理者):定义一个处理请求的接口。通常会包含一个指向下一个处理者的引用(后继者),以及一个处理请求的抽象方法。
  2. ConcreteHandler(具体处理者):实现抽象处理者接口。它需要判断自己是否能处理当前请求。如果能,就进行处理;如果不能,就将请求转发给它的后继者。
  3. Client(客户端):创建并组装处理者链,然后向链的第一个对象发起请求。

结构图

plaintext
+-----------+       +-------------------+       +-------------------+
|  Client   |------>|  ConcreteHandlerA |------>|  ConcreteHandlerB |------> ...
+-----------+       +-------------------+       +-------------------+
                          |                               |
                          | (implements)                  | (implements)
                          ▼                               ▼
                    +----------------+
                    |    Handler     |
                    +----------------+
                    | + setNext()    |
                    | + handle()     |
                    | - nextHandler  |
                    +----------------+

工作流程

  1. 客户端创建一个请求,并将其发送给链的第一个处理者(例如 ConcreteHandlerA)。
  2. 处理者 A 接收到请求,判断自己能否处理。
    • 如果能处理:它就处理该请求,处理流程结束(这是一种“纯”责任链,请求只被一个处理者处理)。
    • 如果不能处理:它会检查是否存在下一个处理者(nextHandler)。
      • 如果存在,它就将请求传递给下一个处理者 B
      • 如果不存在(即它是链的末端),请求可能会被丢弃或进行默认处理。
  3. 处理者 B 重复步骤 2,以此类推,直到请求被处理或整个链都遍历完毕。

代码示例:请假审批流程

这是一个非常经典的责任链模式应用场景。假设一个公司的请假审批流程如下:

  • 项目组长(GroupLeader)可以批准 2 天及以内的假期。
  • 部门经理(DepartmentManager)可以批准 5 天及以内的假期。
  • CEO 可以批准所有假期。

1. 定义请求对象 (LeaveRequest)

java
// 请假请求
public class LeaveRequest {
    private String name;    // 请假人
    private int days;       // 请假天数

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

    public String getName() {
        return name;
    }

    public int getDays() {
        return days;
    }
}

2. 定义抽象处理者 (Approver)

java
// 抽象审批者
public abstract class Approver {
    protected Approver nextApprover; // 下一个审批者

    // 设置下一个审批者,形成链
    public void setNextApprover(Approver nextApprover) {
        this.nextApprover = nextApprover;
    }

    // 抽象的处理方法
    public abstract void approve(LeaveRequest request);
}

3. 创建具体处理者

项目组长 (GroupLeader)

java
public class GroupLeader extends Approver {
    @Override
    public void approve(LeaveRequest request) {
        if (request.getDays() <= 2) {
            System.out.println("项目组长 批准了 " + request.getName() + " 的 " + request.getDays() + " 天假期。");
        } else {
            // 如果自己处理不了,并且有下一个处理者,就转交
            if (nextApprover != null) {
                System.out.println("项目组长 无法处理,转交给上级。");
                nextApprover.approve(request);
            } else {
                System.out.println("审批流程结束,无人能处理该请求。");
            }
        }
    }
}

部门经理 (DepartmentManager)

java
public class DepartmentManager extends Approver {
    @Override
    public void approve(LeaveRequest request) {
        if (request.getDays() <= 5) {
            System.out.println("部门经理 批准了 " + request.getName() + " 的 " + request.getDays() + " 天假期。");
        } else {
            if (nextApprover != null) {
                System.out.println("部门经理 无法处理,转交给上级。");
                nextApprover.approve(request);
            } else {
                System.out.println("审批流程结束,无人能处理该请求。");
            }
        }
    }
}

CEO

java
public class CEO extends Approver {
    @Override
    public void approve(LeaveRequest request) {
        // CEO 可以批准所有请求
        System.out.println("CEO 批准了 " + request.getName() + " 的 " + request.getDays() + " 天假期。");
    }
}

4. 客户端组装并测试

java
public class Client {
    public static void main(String[] args) {
        // 1. 创建处理者
        Approver groupLeader = new GroupLeader();
        Approver manager = new DepartmentManager();
        Approver ceo = new CEO();

        // 2. 组装责任链
        groupLeader.setNextApprover(manager);
        manager.setNextApprover(ceo);

        // 3. 创建并提交请求
        System.out.println("--- 张三请假 1 天 ---");
        groupLeader.approve(new LeaveRequest("张三", 1));
        
        System.out.println("\n--- 李四请假 4 天 ---");
        groupLeader.approve(new LeaveRequest("李四", 4));

        System.out.println("\n--- 王五请假 10 天 ---");
        groupLeader.approve(new LeaveRequest("王五", 10));
    }
}

输出结果:

plaintext
--- 张三请假 1 天 ---
项目组长 批准了 张三 的 1 天假期。

--- 李四请假 4 天 ---
项目组长 无法处理,转交给上级。
部门经理 批准了 李四 的 4 天假期。

--- 王五请假 10 天 ---
项目组长 无法处理,转交给上级。
部门经理 无法处理,转交给上级。
CEO 批准了 王五 的 10 天假期。

优缺点

优点

  1. 降低耦合度:将请求的发送者和接收者解耦。
  2. 增强了系统的可扩展性:可以很容易地增加新的处理者到链中,而无需修改原有代码,符合开闭原则。
  3. 增强了指派职责的灵活性:通过改变链内的成员或者调动它们的次序,允许动态地新增或者删除责任。
  4. 简化了对象:使得每个对象都无需知道链的结构,只需要保存一个指向其后继者的引用。

缺点

  1. 请求不保证被处理:由于请求在链中传递,如果链的末端处理者也无法处理该请求,那么请求就会被丢弃。
  2. 性能问题:在链条比较长的情况下,请求的传递会带来一定的性能开销。
  3. 调试不方便:由于链条是动态组合的,在调试时可能不容易观察到运行时的真实链结构,从而难以定位问题。

适用场景

  1. 有多个对象可以处理同一个请求,但具体由哪个对象处理是在运行时动态确定的。
  2. 你想在不明确指定接收者的情况下,向多个对象中的一个提交一个请求。
  3. 需要动态指定一组对象处理请求的顺序。

现实世界中的例子

  • Java Web 中的 Servlet FilterFilterChain 就是一个典型的责任链。每个 Filter 都可以对 RequestResponse 进行处理,然后调用 chain.doFilter() 将请求传递给下一个 Filter。这是一种变体,因为每个过滤器都会执行,而不仅仅是找到第一个能处理的就停止。
  • Web 框架的中间件(Middleware):例如 Express.js、ASP.NET Core 中的中间件管道,请求会依次通过管道中的每个中间件,每个中间件都可以处理请求、修改请求或提前返回响应。
  • UI 事件冒泡:在前端开发中,一个事件(如点击)会从最内层的元素开始,逐级向上传播到父元素,每个元素都可以捕获并处理这个事件。

总结

责任链模式的核心在于解耦灵活性。它将处理逻辑分布到一串独立的对象中,使得系统在应对需求变化时,能够通过重组这条“链”来快速适应,而不需要修改核心业务代码。

右滑查看面试常问