image.png

责任链模式 Chain Of Responsibility Design Pattern

Avoid coupling the sender of a request to its receiver by giving more than one object a chance to handle the request. Chain the receiving objects and pass the request along the chain until an object handles it.——GoF
将请求的发送和接收解耦,让多个接收对象都有机会处理这个请求。将这些接收对象串成一条链,并沿着这条链传递这个请求,直到链上的某个接收对象能够处理它为止。

将多个对象组成一条职责链,然后按照它们在职责链上的顺序一个一个地找出到底应该谁来负责处理。使用责任链模式可以弱化“请求方”和“处理方”之间的关联关系。

登场角色:

  • Handler(处理者):Handler 角色定义了处理请求的接口(API)。Handler 角色知道“下一个处理者”是谁,如果自己无法处理请求,它会将请求转给“下一个处理者”。当然,“下一个处理者”也是 Handler 角色。
  • ConcreteHandler(具体的处理者):ConcreteHandler 角色是处理请求的具体角色。
  • Client(请求者):Client 角色是向第一个 ConcreteHandler 角色发送请求的角色。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Client
abstract class Handler{
next
{method} {abstract} request
}

class ConcreteHandler1{
{method} request
}

class ConcreteHandler2{
{method} request
}

Client -> Handler : Request >
Handler o--> Handler
Handler <|-- ConcreteHandler1
Handler <|-- ConcreteHandler2

request() 方法中一般写明自己的处理逻辑,以及调用字段 next 去处理的逻辑。具体代码详看接下来的例子。

特点:

  • 弱化了发出请求的人和处理请求的人之间的关系。「发出请求的人」不需要知道处理请求的人各自责任分担情况。
  • 同模板模式、策略模式一样,责任链模式利于代码的复用和扩展
    • 配置灵活:可以动态改变职责链
    • 应对代码复杂性:每个模块专注于自己的工作

缺点:

  • 推卸请求可能会导致处理延迟。需要自己权衡。

相关的设计模式:

  • Composite 模式 (第 11 章):Handler 角色经常会使用 Composite 模式。
  • Command 模式 (第 23 章):有时会使用 Command 模式向 Handler 角色发送请求。

例子——构造责任链的细节

名字 说明
Trouble 表示发生的问题的类。它带有问题编号(number)
Support 用来解决问题的抽象类
NoSupport 用来解决问题的具体类 (永远“不处理问题”)
LimitSupport 用来解决问题的具体类(仅解决编号小于指定编号的问题)
Specialsupport 用来解决问题的具体类(仅解决指定编号的问题)
Main 制作 support 的职责链,制造问题并测试程序行为
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
class Main
abstract class Support{
- name
- next
+ {method} support
+ {method} setNext
# {method} {abstract} resolve
}

class NoSupport{
# {method} resolve
}

class LimitSupport{
- limit
# {method} resolve
}

class SpecialSupport{
- number
# {method} resolve
}

Main -> Support : Request >
Support o--> Support
Support <|-- NoSupport
Support <|-- LimitSupport
Support <|-- SpecialSupport

下面给出完整代码案例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Trouble {
private final int number; // 问题编号
public Trouble(int number){
this.number = number;
}
public int getNumber(){
return number;
}

@Override
public String toString(){
return "[问题编号:"+number+"]";
}
}

Support 抽象类代码如下。public void support(Trouble trouble) 方法采用了模板模式。

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
// Support 抽象类
public abstract class Support{
private final String name;
private Support next; // 要推卸给的对象。通过setNext函数设置

public void support(Trouble trouble) {
if (resolve(trouble)) {
done(trouble);
} else if (next != null) {
next.support(trouble); // 解决不了问题就递归调用链条
} else {
fail(trouble);
}
}

// 重要技巧
public Support setNext(Support next){
this.next = next;
return next;
}

public void setNextButNotReturn(Support next){
this.next = next;
}

public Support(String name){
this.name = name;
}

@Override
public String toString(){
return "[" + name + "]";
}

protected abstract boolean resolve(Trouble trouble);

protected void done (Trouble trouble){
System.out.println(trouble+" 已经被 "+this+"解决");
}

protected void fail (Trouble trouble){
System.out.println(trouble+" 无法被 "+this+"解决");
}
}

Support 的实现类:

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
// Support 的实现类
public class NoSupport extends Support{

public NoSupport(String name) {
super(name);
}

@Override
protected boolean resolve(Trouble trouble) {
return false;
}
}


public class LimitSupport extends Support{
private final int limit;

public LimitSupport(String name, int limit) {
super(name);
this.limit = limit;
}

@Override
protected boolean resolve(Trouble trouble) {
return trouble.getNumber() < limit;
}
}

public class SpecialSupport extends Support{
private final int number;

public SpecialSupport(String name, int number) {
super(name);
this.number = number;
}

@Override
protected boolean resolve(Trouble trouble) {
return trouble.getNumber() == number;
}
}

调用方代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
public static void main(String[] args) {
Support s1 = new NoSupport("不予通过方案");
Support s2 = new LimitSupport("不及格方案",60);
Support s3 = new SpecialSupport("满分方案",100);

// 责任链形成s1->s2->s3:
s1.setNext(s2).setNext(s3);

// 用这条链处理以下 3 个问题:
s1.support(new Trouble(13)); // [问题编号:13] 已经被 [不及格方案]解决
s1.support(new Trouble(64)); // [问题编号:64] 无法被 [满分方案]解决
s1.support(new Trouble(100));// [问题编号:100] 已经被 [满分方案]解决
}

重要技巧:Support 抽象类中 setNext() 除了设置 next 外,还 return next;。这样可以有

h1.setNext(h2).setNext(h3).setNext(h4).setNext(h5) 这样的责任链构造。

责任链构造的错误示例

请注意,这种责任链构造方式是错误的h1.setNext(h2.setNext(h3.setNext(h4.setNext(h5)))); 这会导致构造这样一个责任链:h1->h5(想一想为什么?)

如果 setNext() 中单纯设置 next,不 return next; 的话,责任链构造方式只能是这个样子: h1.setNext(h2); h2.setNext(h3)

变体:引入责任链构造类

在上面的代码中,我们直接在 main 函数中使用责任链的头结点 s1 不断进行构造。我们可以引用新的类 SupportChain 来管理这个链条。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class SupportChain {
// 记录链条的首尾结点
private Support head = null;
private Support tail = null;

public void addSupport(Support support) {
support.setNext(null);

if (head == null) {
head = support;
tail = support;
return;
}

tail.setNext(support);
tail = support;
}

public void support() {
if (head != null) {
head.handle();
}
}
}

使用示例:

1
2
3
4
5
6
public static void main(String[] args) {
SupportChain chain = new SupportChain();
chain.addSupport(new LimitSupport("不及格方案",60));
chain.addSupport(new SpecialSupport("满分方案",100));
chain.support();
}

我们还可以用数组而非链表来保存所有的处理器,然后在 SupportChain 中依次调用即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class SupportChain {
private List<Support> supports = new ArrayList<>();

public void addSupport(Support support) {
this.supports.add(support);
}

public void support() {
for (Support support: supports) {
boolean isSoveled = support.support();
if (isSoveled) {
break;
}
}
}
}

变体:中途不终止链条

在 GoF 给出的定义中,如果处理器链上的某个处理器能够处理这个请求,就不会继续往下传递请求。实际上职责链模式还有一种变体:请求会被所有的处理器都处理一遍,不存在中途终止的情况。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// Support 抽象类
public abstract class Support{
private final String name;
private Support next; // 要推卸给的对象。通过setNext函数设置

public void support(Trouble trouble) {
resolve(trouble);
// 无视结果,继续处理
if (next != null) {
next.support(trouble);
}
}

// 省略其他代码
}

本文参考