职责链模式

设计模式笔记:职责链模式 (Chain of Responsibility Pattern)

一、一句话概括

为请求创建了一个接收者对象的链,并让请求在链上传递,直到链上的某个接收者处理它为止。这避免了将请求的发送者接收者耦合在一起。

二、为什么需要它 (Why - The Pain Point)

想象一个公司的费用报销审批流程:

  • 金额小于等于 500 元,项目经理(Team Leader) 可以直接审批。
  • 金额大于 500 元但小于等于 5000 元,需要部门主管(Department Head) 审批。
  • 金额大于 5000 元,需要 CEO 审批。

如果你用传统的 if-elseswitch 语句来写这个逻辑:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class ExpenseReport { double amount; }

// 痛点:一个集中的处理类,充满了 if-else 逻辑
class ApprovalSystem {
public void approve(ExpenseReport report) {
if (report.getAmount() <= 500) {
// 项目经理处理逻辑
System.out.println("Approved by Team Leader.");
} else if (report.getAmount() <= 5000) {
// 部门主管处理逻辑
System.out.println("Approved by Department Head.");
} else {
// CEO 处理逻辑
System.out.println("Approved by CEO.");
}
}
}

痛点:僵化且难以维护

  1. 违反开闭原则:如果公司的审批流程发生变化,比如在部门主管和 CEO 之间增加一个“总监(Director)”级别,你就必须修改 ApprovalSystem 类的 approve 方法,增加一个新的 else if 分支。
  2. 高耦合ApprovalSystem 这个类耦合了所有的审批逻辑,变得非常臃肿。请求的提交者(比如员工)也直接依赖于这个集中的处理系统。
  3. 职责不单一ApprovalSystem 承担了所有级别的审批职责。
  4. 灵活性差:无法在运行时动态地调整审批流程或顺序。

核心问题:如何让多个对象都有机会处理一个请求,同时又避免请求的发送者和接收者之间产生紧密的、硬编码的联系?

三、它是什么 (What - The Solution)

职责链模式将这些“有能力处理请求的对象”连接成一条链。当一个请求到来时,它会沿着这条链进行传递,链上的每个对象都有机会处理它。

核心思想和角色:

  1. **处理器接口/抽象类 (Handler)**:
    • 定义了处理请求的接口,通常包含一个 handleRequest(Request request) 方法。
    • 关键部分:它还包含一个指向下一个处理器(successor的引用。
    • 它提供了一个默认的实现:如果自己不能处理该请求,就将请求传递给下一个处理器,即 successor.handleRequest(request)
  2. **具体处理器 (Concrete Handler)**:
    • 实现了处理器接口。
    • 它负责处理它感兴趣的请求。
    • handleRequest 方法中,它会先判断自己是否能处理这个请求。
      • 如果,就处理它,然后可以选择是否继续向下传递。
      • 如果不能,就直接将请求转发给它的后继者。
  3. **客户端 (Client)**:
    • 客户端负责创建和组装这条职责链。
    • 它只需要将请求发送给链的第一个处理器,然后就不再关心请求由谁、以及如何被处理。

工作流程:

  1. 客户端创建一个报销请求。
  2. 客户端将请求发送给审批链的第一个节点(项目经理)。
  3. 项目经理检查金额:
    • 如果金额 <= 500,处理它,流程结束。
    • 如果金额 > 500,他处理不了,于是将请求传递给他的后继者(部门主管)。
  4. 部门主管检查金额:
    • 如果金额 <= 5000,处理它,流程结束。
    • 如果金额 > 5000,他处理不了,于是将请求传递给他的后继者(CEO)。
  5. CEO 拥有最高权限,处理请求,流程结束。

四、怎么做 (How - The Blueprint)

基本步骤(以费用审批为例):

  1. **创建请求类 ExpenseReport**,包含金额等信息。
  2. **创建处理器抽象类 Approver**。
    • 包含一个 Approver successor 字段。
    • 定义一个抽象方法 handleRequest(ExpenseReport report)
  3. 创建具体处理器类,如 TeamLeader, DepartmentHead, CEO,它们都继承自 Approver
    • 在各自的 handleRequest 方法中,实现自己的处理逻辑和转发逻辑。
  4. 客户端创建处理器实例,并通过 setSuccessor() 方法将它们链接起来,形成一条完整的审批链。
  5. 客户端将报销请求发送给链的头部。

Java 示例代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
// 1. 请求类
class ExpenseReport {
private final String title;
private final double amount;

public ExpenseReport(String title, double amount) {
this.title = title;
this.amount = amount;
}
public double getAmount() { return amount; }
public String getTitle() { return title; }
}

// 2. 处理器抽象类 (Handler)
abstract class Approver {
protected Approver successor; // 指向后继者

public void setSuccessor(Approver successor) {
this.successor = successor;
}

// 抽象的处理方法
public abstract void handleRequest(ExpenseReport report);
}

// 3. 具体处理器 (Concrete Handler)
class TeamLeader extends Approver {
@Override
public void handleRequest(ExpenseReport report) {
if (report.getAmount() <= 500) {
System.out.println("Team Leader approved: " + report.getTitle());
} else if (successor != null) {
System.out.println("Team Leader cannot approve. Passing to successor.");
successor.handleRequest(report);
}
}
}

class DepartmentHead extends Approver {
@Override
public void handleRequest(ExpenseReport report) {
if (report.getAmount() <= 5000) {
System.out.println("Department Head approved: " + report.getTitle());
} else if (successor != null) {
System.out.println("Department Head cannot approve. Passing to successor.");
successor.handleRequest(report);
}
}
}

class CEO extends Approver {
@Override
public void handleRequest(ExpenseReport report) {
// CEO是最高级别,总能处理
System.out.println("CEO approved: " + report.getTitle());
}
}

// 4. 客户端
public class ChainOfResponsibilityDemo {
public static void main(String[] args) {
// 创建处理器
Approver leader = new TeamLeader();
Approver head = new DepartmentHead();
Approver ceo = new CEO();

// 组装职责链
leader.setSuccessor(head);
head.setSuccessor(ceo);

// 创建并发送请求
ExpenseReport report1 = new ExpenseReport("Team building", 300);
System.out.println("--- Processing report 1 (amount: 300) ---");
leader.handleRequest(report1); // 发送给链头

System.out.println();

ExpenseReport report2 = new ExpenseReport("Purchase new server", 4500);
System.out.println("--- Processing report 2 (amount: 4500) ---");
leader.handleRequest(report2);

System.out.println();

ExpenseReport report3 = new ExpenseReport("Company acquisition", 100000);
System.out.println("--- Processing report 3 (amount: 100000) ---");
leader.handleRequest(report3);
}
}

五、UML 类图

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
@startuml
skinparam classAttributeIconSize 0
hide empty members

abstract class Approver {
# Approver successor
+ void setSuccessor(Approver s)
+ {abstract} void handleRequest(ExpenseReport r)
}

class TeamLeader extends Approver {
+ void handleRequest(ExpenseReport r)
}
class DepartmentHead extends Approver {
+ void handleRequest(ExpenseReport r)
}
class CEO extends Approver {
+ void handleRequest(ExpenseReport r)
}

class ExpenseReport {}

' Relationship: The chain
Approver o-- "1" Approver : successor >

class ChainOfResponsibilityDemo {}
ChainOfResponsibilityDemo ..> Approver : uses
ChainOfResponsibilityDemo ..> ExpenseReport : creates
@enduml

图示解读:

  • TeamLeader, DepartmentHead, CEO 都继承自 Approver
  • Approver 内部有一个指向自己的引用 successor,这构成了链式结构。
  • 客户端只与链的头部(一个 Approver)交互。

六、优缺点分析

优点 (Pros):

  1. 降低耦合度:请求的发送者完全不知道是哪个接收者处理了请求,也不知道链的结构。接收者也无需知道发送者的信息。
  2. 增强了灵活性:可以随时增加、删除或重新排序链上的处理器,而无需修改客户端代码或其他处理器的代码,符合开闭原则。
  3. 职责分配更清晰:每个处理器只关心自己能处理的请求,职责明确。

缺点 (Cons):

  1. 请求不保证被处理:如果链的构造不当,或者没有任何处理器能够处理该请求,那么请求可能会传递到链的末尾而未被处理。
  2. 调试困难:由于请求在链中传递,如果链条很长,追踪请求的处理过程可能会变得复杂。
  3. 可能影响性能:请求需要遍历链上的多个节点,可能会有一定的性能开销。

七、在 Java 中的应用

  • **Java Servlet 过滤器 (Filter Chain)**:这是职责链模式最经典、最完美的工业级应用。
    • 当一个 HTTP 请求到达 Web 服务器时,它会经过一个由多个 Filter 组成的链(FilterChain)。
    • 每个 Filter(具体处理器)都可以对请求进行检查或修改(如编码转换、权限验证、日志记录)。
    • 如果 Filter 决定继续处理,它会调用 chain.doFilter(request, response) 将请求传递给链上的下一个 Filter 或最终的 Servlet
    • 你可以通过 web.xml 或注解来配置这个过滤器链,非常灵活。
  • Java 异常处理机制 (try-catch): 在某种程度上,Java 的 try-catch 块也体现了职责链的思想。当一个异常被抛出时,它会沿着调用栈向上“传递”,直到找到第一个能够捕获并处理该类型异常的 catch 块。
  • Log4j / Logback 的 Appender: 日志框架中的 Appender 也可以形成一个链,一个日志事件可以被多个 Appender 处理(比如同时输出到控制台和文件)。