Z

责任链模式(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。

这样的设计有什么好处?显然在这个例子中看不出来,但在一些复杂的逻辑,并且还涉及多个系统的处理逻辑,责任链模式就有其用武之地了,典型的就是注册逻辑。

下面是一个复杂的注册逻辑:

  • 输入邮箱、密码注册
  • 验证邮箱
  • 输入手机号
  • 验证手机号
  • 绑定信用卡
  • 注册成功

这一长串流程,显然不是一个函数加多个条件判断就能处理的了的,并且他还包含了多个异步系统之间的协同工作。而且用户在执行到某一步时中断了,重新注册时还要恢复到上次处理的逻辑。

在这个系统中,每一步都会产生一个不同的状态,那么使用责任链模式就可以清晰的定义和解耦上面的处理流程,使整个系统的可维护性增强,而且每段逻辑都是互不依赖的,每个责任只需要关心自己的一亩三分地。

但责任链模式也不是万能的,从代码的微观角度来说,责任链模式就是一个链表,那么一个逻辑很可能就得从头至尾遍历才能得到真正的结果,这有可能会导致性能问题。当然你也可以将链式结构改造成哈希表结构,那么整个系统就会从链向状态机转移。由于链条变长,调试也可能会变得麻烦。所以实际应用中,最好限制责任链的最大长度。