责任链模式(Chain-of-responsibility 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.
责任链模式,顾名思义,该模式中有一条“链”,就像链表一样。在一个系统中,可以有多个不同的逻辑来处理同一种对象。那么具体是由那种逻辑来处理,一般是根据对象上的某一表示标识,例如这样的代码:
1enum State {
2 START,
3 PROCESSING,
4 END
5}
6
7abstract class ItemCls {
8 abstract state: State;
9}
10
11class Item extends ItemCls {
12 constructor(public state: State) {
13 super();
14 }
15}
16
17function process(item: ItemCls) {
18 if (item.state === State.START) {
19 console.log('start logic');
20 return;
21 }
22
23 if (item.state === State.PROCESSING) {
24 console.log('processing logic');
25 return;
26 }
27
28 if (item.state === State.END) {
29 console.log('end logic');
30 return;
31 }
32}
33
34process(new Item(State.START));
35process(new Item(State.PROCESSING));
36process(new Item(State.END));
在process过程中,我们根据item的state属性判断使用哪种逻辑。上面的代码并没有使用责任链模式,对于这种简单的需求,单纯的使用条件判断就好,如果强行使用责任链,那就是过度设计了。
那么到底什么是责任链模式?责任链是一种对于一长串处理过程的抽象,就像下面这样:
如果满足logic A的条件,则logic A处理,否则交给下一个处理逻辑,如此不断执行,直到责任链结束,那么上面的代码使用责任链模式就可以这么改写:
1enum State {
2 START,
3 PROCESSING,
4 END
5}
6
7abstract class ItemCls {
8 abstract state: State;
9}
10
11class Item extends ItemCls {
12 constructor(public state: State) {
13 super();
14 }
15}
16
17
18abstract class Logic {
19 private next: Logic = null;
20
21 handle(item: ItemCls) {
22 if (this.getHandleLevel() === item.state) {
23 this.response(item);
24 } else {
25 this.next.handle(item);
26 }
27 }
28
29 setNext(next: Logic) {
30 this.next = next;
31 }
32
33 abstract getHandleLevel(): State;
34 abstract response(item: ItemCls): void;
35}
36
37class LogicA extends Logic {
38 getHandleLevel() {
39 return State.START
40 }
41
42 response(item: ItemCls) {
43 console.log('start logic');
44 }
45}
46
47class LogicB extends Logic {
48 getHandleLevel() {
49 return State.PROCESSING
50 }
51
52 response(item: ItemCls) {
53 console.log('process logic');
54 }
55}
56
57class LogicC extends Logic {
58 getHandleLevel() {
59 return State.END
60 }
61
62 response(item: ItemCls) {
63 console.log('end logic');
64 }
65}
66
67const logicA = new LogicA();
68const logicB = new LogicB();
69const logicC = new LogicC();
70
71logicA.setNext(logicB);
72logicB.setNext(logicC);
73
74logicA.handle(new Item(State.START));
75logicA.handle(new Item(State.PROCESSING));
76logicA.handle(new Item(State.END));
在上面的代码中,根据抽象类Logic创建了不同的实体类Logic,每个实体Logic只需要实现getHandleLevel与response方法即可。在代码最后,使用抽象类中的setNext方法设定该逻辑的下一条是什么。在最后执行中,从logicA的handle方法开始,如果满足logicA的条件,则logicA工作,否则进入logicB,如果还不满足,则进入最后的logicC。
这样的设计有什么好处?显然在这个例子中看不出来,但在一些复杂的逻辑,并且还涉及多个系统的处理逻辑,责任链模式就有其用武之地了,典型的就是注册逻辑。
下面是一个复杂的注册逻辑:
- 输入邮箱、密码注册
- 验证邮箱
- 输入手机号
- 验证手机号
- 绑定信用卡
- 注册成功
这一长串流程,显然不是一个函数加多个条件判断就能处理的了的,并且他还包含了多个异步系统之间的协同工作。而且用户在执行到某一步时中断了,重新注册时还要恢复到上次处理的逻辑。
在这个系统中,每一步都会产生一个不同的状态,那么使用责任链模式就可以清晰的定义和解耦上面的处理流程,使整个系统的可维护性增强,而且每段逻辑都是互不依赖的,每个责任只需要关心自己的一亩三分地。
但责任链模式也不是万能的,从代码的微观角度来说,责任链模式就是一个链表,那么一个逻辑很可能就得从头至尾遍历才能得到真正的结果,这有可能会导致性能问题。当然你也可以将链式结构改造成哈希表结构,那么整个系统就会从链向状态机转移。由于链条变长,调试也可能会变得麻烦。所以实际应用中,最好限制责任链的最大长度。