本博客大部分内容来于免费在线学习设计模式

1:责任链模式

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

2:责任链模式问题

在日常生活中,请假,出差,申请活动等等。一件事可能需要经过多个对象的处理。举个例子:你写了一个办公程序,最初设置的是只需要部门主管同意,后面又逐渐增加人事,领导等等很多步骤。现在需要另外增加另一项功能,也需要这些审批手续(也可能是部分)。这一段代码怎么写呢?直接复制代码吗,本来每次增加步骤都会使代码更加臃肿,再对这些臃肿的代码进行复制,系统会变成怎样呢?

3:责任链模式解决方案

责任链会将特定行为转换为被称作处理者的独立对象。在上述示例中,每个审批步骤都可被抽取为仅有单个方法的类,并执行审批操作。 请求及其数据则会被作为参数传递给该方法。

模式建议你将这些处理者连成一条链。链上的每个处理者都有一个成员变量来保存对于下一处理者的引用。除了处理请求外,处理者还负责沿着链传递请求。请求会在链上移动,直至所有处理者都有机会对其进行处理。

最重要的是:处理者可以决定不再沿着链传递请求,这可高效地取消所有后续处理步骤。

在之前的例子中就相当于:只要发起请求,都先提交给主管,满足条件主管就转给人事……像一条链条一样,如果中间某一环节不满足,链条就断开了,不再调用下去。

又或者:当你使用某一程序出现问题与客服沟通时,客服会先自行解决这个问题,如果不能解决,客服可以将这一请求委托给技术人员进行处理。

责任链模式定义:为了避免请求发送者与多个请求处理者耦合在一起,于是将所有请求的处理者通过前一对象记住其下一个对象的引用而连成一条链;当有请求发生时,可将请求沿着这条链传递,直到有对象处理它为止。

在责任链模式中,客户只需要将请求发送到责任链上即可,无须关心请求的处理细节和请求的传递过程,请求会自动进行传递。所以责任链将请求的发送者和请求的处理者解耦了。

4:责任链模式结构

20201127141108

  1. 处理者 (Handler) 声明了所有具体处理者的通用接口。 该接口通常仅包含单个方法用于请求处理, 但有时其还会包含一个设置链上下个处理者的方法。
  2. 基础处理者 (Base Handler) 是一个可选的类, 你可以将所有处理者共用的样本代码放置在其中。
    通常情况下, 该类中定义了一个保存对于下个处理者引用的成员变量。 客户端可通过将处理者传递给上个处理者的构造函数或设定方法来创建链。 该类还可以实现默认的处理行为: 确定下个处理者存在后再将请求传递给它。
  3. 具体处理者 (Concrete Handlers) 包含处理请求的实际代码。 每个处理者接收到请求后, 都必须决定是否进行处理, 以及是否沿着链传递请求。
    处理者通常是独立且不可变的, 需要通过构造函数一次性地获得所有必要地数据。
  4. 客户端 (Client) 可根据程序逻辑一次性或者动态地生成链。 值得注意的是, 请求可发送给链上的任意一个处理者, 而非必须是第一个处理者。

代码示例:

模拟申请假期,2天以下班主任可以开,超出2天由班主任交给年级主任开具,超出7天有年级主任提交给系主任。

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
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
/**
* 处理器接口
*/
interface Handler {
/**
* 通用方法-审批请假天数
* @param days
*/
void handleRequest(int days);
}

/**
* 抽象领导类
*/
abstract class Leader implements Handler {
/**
* 下一级领导
*/
private Leader next;

Leader() {
}

Leader(Leader leader) {
this.next = leader;
}

public Leader getNext() {
return next;
}

public void setNext(Leader next) {
this.next = next;
}
}


/**
* 具体领导1:班主任
*/
class ClassTeacher extends Leader {
@Override
public void handleRequest(int days) {
if (days <= 2) {
System.out.println("班主任批准:" + days + "天假期");
} else {
if (getNext() != null) {
getNext().handleRequest(days);
} else {
System.out.println(days + "假期无法批准");
}
}
}
}

/**
* 具体领导2:年级主任
*/
class GradeDirector extends Leader {
@Override
public void handleRequest(int days) {
if (days <= 7) {
System.out.println("年纪主任批准:" + days + "天假期");
} else {
if (getNext() != null) {
getNext().handleRequest(days);
} else {
System.out.println(days + "假期无法批准");
}
}
}
}

/**
* 具体领导3:系主任
*/
class DepartmentHead extends Leader {
@Override
public void handleRequest(int days) {
if (days <= 15) {
System.out.println("系主任批准:" + days + "天假期");
} else {
if (getNext() != null) {
getNext().handleRequest(days);
} else {
System.out.println(days + "天假期无法批准");
}
}
}
}

public class CoR {
public static void main(String[] args) {
Leader classTeacher = new ClassTeacher();
Leader gradeDirector = new GradeDirector();
Leader departmentHead = new DepartmentHead();
classTeacher.setNext(gradeDirector);
gradeDirector.setNext(departmentHead);

classTeacher.handleRequest(2);
classTeacher.handleRequest(7);
classTeacher.handleRequest(8);
classTeacher.handleRequest(16);
}
}

运行结果如下:

1
2
3
4
班主任批准:2天假期
年纪主任批准:7天假期
系主任批准:8天假期
16天假期无法批准

你可以添加后面更多的处理步骤,而不会过多的影响程序性能。

5:责任链模式适用场景

  1. 当程序需要使用不同方式处理不同种类请求,而且请求类型和顺序预先未知时,可以使用责任链模式。
  2. 当必须按顺序执行多个处理者时, 可以使用该模式。
  3. 如果所需处理者及其顺序必须在运行时进行改变,可以使用责任链模式。

6:责任链模式优缺点

优点 缺点
你可以控制请求处理的顺序。 部分请求可能未被处理。
单一职责原则。 你可对发起操作和执行操作的类进行解耦。
开闭原则。 你可以在不更改现有代码的情况下在程序中新增处理者。

7:责任链模式与其他模式关系

  • 责任链模式、命令模式、中介者模式和观察者模式用于处理请求发送者和接收者之间的不同连接方式:
    • 责任链按照顺序将请求动态传递给一系列的潜在接收者,直至其中一名接收者对请求进行处理。
    • 命令在发送者和请求者之间建立单向连接。
    • 中介者清除了发送者和请求者之间的直接连接,强制它们通过一个中介对象进行间接沟通。
    • 观察者允许接收者动态地订阅或取消接收请求。
  • 责任链通常和组合模式结合使用。在这种情况下,叶组件接收到请求后,可以将请求沿包含全体父组件的链一直传递至对象树的底部。
  • 责任链的管理者可使用命令模式实现。在这种情况下,你可以对由请求代表的同一个上下文对象执行许多不同的操作。
    还有另外一种实现方式,那就是请求自身就是一个命令对象。在这种情况下,你可以对由一系列不同上下文连接而成的链执行相同的操作。
  • 责任链和装饰模式的类结构非常相似。两者都依赖递归组合将需要执行的操作传递给一系列对象。但是,两者有几点重要的不同之处。
    责任链的管理者可以相互独立地执行一切操作,还可以随时停止传递请求。另一方面,各种装饰可以在遵循基本接口的情况下扩展对象的行为。此外,装饰无法中断请求的传递。

8:责任链模式举例

还是请假:先列出来公用的处理方式-审批假期,然后定义基础代码-领导,最后是创建具体领导,首先是班主任,先找个人当班主任,然后设置班主任有事就报告给年级主任,最后设置班主任的审批范围。以此类推,设置好后续领导,请假的链式审批就设置好了。

9:参考