javascript设计模式八:职责链模式

2019-08-29 13:14:58 浏览数 (1)

职责链的定义:使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系,将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象能处理它为止,传递链中的这些对象就叫节点。

需求背景: 一个电商网站,用户交500定金且定金已付时,可享受500优惠券且不受货物数量限制;用户交200定金且定金已付时,可享受500优惠券且不受货物数量限制;用户不交定金时受货物数量限制,有货时原价买,无货时则无法买。

原始版本, if else一路判断

代码语言:javascript复制
 1var buyOrder = function(orederType, pay, stock){
 2    if(orederType == 1){
 3        if(pay){
 4            console.log('500优惠券');
 5        }else {
 6            if(stock > 0){
 7                console.log('普通购物页面');
 8            }else {
 9                console.log('已无货');
10            }
11        }
12    }else if(orederType == 2){
13        if(pay){
14            console.log('200优惠券');
15        }else {
16            if(stock > 0){
17                console.log('普通购物页面');
18            }else {
19                console.log('已无货');
20            }
21        }
22    }else if(orederType == 3){
23        if(stock > 0){
24            console.log('普通购物页面');
25        }else {
26            console.log('已无货');
27        }
28    }
29}
30
31buyOrder(1, true, 600)

改进版本

代码语言:javascript复制
 1var order500 = function(orderType, pay , stock){
 2    if(orderType == '1' && pay == true){
 3        console.log('500优惠券');
 4    }else {
 5        order200(orderType, pay , stock)
 6    }
 7}
 8
 9var order200 = function(orderType, pay , stock){
10    if(orderType == '2' && pay == true){
11        console.log('200优惠券');
12    }else {
13        orderNormal(orderType, pay , stock)
14    }
15}
16
17var orderNormal = function(orderType, pay , stock){
18    if(stock > 0){
19        console.log('普通购物页面');
20    }else {
21        console.log('已无货');
22    }
23}
24
25order500(3, true, 0)

优化版本1: 同步的职责链

代码语言:javascript复制
 1//3个订单函数 ,它们都是节点函数
 2var order500 = function(orderType, pay , stock){
 3    if(orderType == '1' && pay == true){
 4        console.log('500优惠券');
 5    }else {
 6        return 'nextSuccessor';     //我不知道下个节点是谁,反正把请求往后传递
 7    }
 8}
 9
10var order200 = function(orderType, pay , stock){
11    if(orderType == '2' && pay == true){
12        console.log('200优惠券');
13    }else {
14        return 'nextSuccessor';     //我不知道下个节点是谁,反正把请求往后传递
15    }
16}
17
18var orderNormal = function(orderType, pay , stock){
19    if(stock > 0){
20        console.log('普通购物页面');
21    }else {
22        console.log('已无货');
23    }
24}
25
26//职责构造函数
27var Chain = function(fn){
28    this.fn = fn;
29    this.successor = null;
30}
31
32Chain.prototype.setNextSuccessor = function(successor){     //设置职责顺序方法
33    this.successor = successor
34}
35
36Chain.prototype.passRequest = function(){       //请求传递
37    var ret = this.fn.apply(this, arguments)
38
39    if(ret === 'nextSuccessor'){
40        return this.successor && this.successor.passRequest.apply(this.successor, arguments)
41    }
42
43    return ret;
44}
45
46//把3个订单函数分别包装成职责链的节点
47var chainOrder500 = new Chain(order500)
48var chainOrder200 = new Chain(order200)
49var chainOrderNormal = new Chain(orderNormal)
50
51//然后指定节点在职责链中的顺序
52chainOrder500.setNextSuccessor(chainOrder200)
53chainOrder200.setNextSuccessor(chainOrderNormal)
54
55//最后把请求传递给第一个节点,开启职责链模式传递
56chainOrder500.passRequest(1, true, 500)     //500优惠券
57chainOrder500.passRequest(3, true, 20)      //普通购物页面
58chainOrder500.passRequest(3, true, 0)       //已无货
59
60//此时如果中间有需求改动,只需如此做: 
61var order300 = function(){
62    if(orderType == '3' && pay == true){
63        console.log('300优惠券');
64    }else {
65        return 'nextSuccessor';     //我不知道下个节点是谁,反正把请求往后传递
66    }
67}
68var chainOrder300 = new Chain(order300)     //添加新职责节点
69chainOrder500.setNextSuccessor(chainOrder300)
70chainOrder300.setNextSuccessor(chainOrder300)   //修改职责链顺序
71chainOrder200.setNextSuccessor(chainOrderNormal)
72
73//这样,就可以完全不必去理会原来的订单函数代码,只需增加一个节点,然后重新设置职责链中的相关节点的顺序就行。

优化版本2:异步的职责链

在实际开发中,经常会遇到 一些异步的问题,比如要在节点函数中发起一个ajax请求,异步请求返回的结果才能决定是否继续在职责链中passRequest

可以给Chain类再增加一个原型方法:

代码语言:javascript复制
 1//职责构造函数
 2var Chain = function(fn){
 3    this.fn = fn;
 4    this.successor = null;
 5}
 6
 7Chain.prototype.setNextSuccessor = function(successor){     //设置职责顺序方法
 8    this.successor = successor
 9}
10
11Chain.prototype.passRequest = function(){       //请求传递
12    var ret = this.fn.apply(this, arguments)
13
14    if(ret === 'nextSuccessor'){    //传递给职责链中的下一个节点
15        return this.successor && this.successor.passRequest.apply(this.successor, arguments)
16    }
17
18    return ret;
19}
20
21//新增,表示手动传递请求给职责链中的下一个节点
22Chain.prototype.next = function(){
23    return this.successor && this.successor.passRequest.apply(this.successor, arguments)
24}
25
26
27//异步职责链例子
28var fn1 = new Chain(function(){
29    console.log(1);
30    return 'nextSuccessor'
31})
32
33var fn2 = new Chain(function(){
34    console.log(2);
35    var self = this;
36    setTimeout(function(){
37        self.next()
38    }, 1000)
39})
40
41var fn3 = new Chain(function(){
42    console.log(3);
43})
44
45
46//指定节点在职责链中的顺序
47fn1.setNextSuccessor(fn2)
48fn2.setNextSuccessor(fn3)
49
50//把请求传递给第一个节点,开始节点传递
51fn1.passRequest()
52
53//输出 1 2 ...(1秒后)... 3
54
55//这是一个异步职责链,请求在职责链节点中传递,但节点有权利决定什么时候 把请求交给下一个节点。这样可以创建一个异步ajax队列库。 

tips:

这里补充个知识点:"短路求值" && 会返回第一个假值(0, null, "", undefined, NaN),而 || 则会返回第一个真值。

var x = a || b || c 等价于:

代码语言:javascript复制
1var x;
2if(a){
3    x = a;
4} else if(b){
5    x = b;
6} else {
7    x = c;
8}

var x = a && b && c 等价于:

代码语言:javascript复制
1var x = a;
2if(a){
3    x = b;
4    if(b){
5        x = c;
6    }
7}

所以 && 有时候会用来代替 if (expression) doSomething(),转成 &&方式就是 expression && doSomething()

|| 比较用来在函数中设置默认值,比如:

代码语言:javascript复制
1function doSomething(arg1, arg2, arg3) {
2    arg1 = arg1 || 'arg1Value';
3    arg2 = arg2 || 'arg2Value';
4}

不过还需要看具体的使用场景,就比如如果要求 doSomething() 传入的 arg1 为一个数值,则上面的写法就会出现问题(在传入 0 的时候被认为是一个假值而使用默认值)。

现在个人比较常用的方法只判断是否与 undefined 相等,比如

代码语言:javascript复制
1function doSomething(arg) {
2    arg = arg !== void 0 ? arg : 0;
3}

职责链模式的优势:解耦请求发送者和N个接收者之间的复杂关系,由于不知道链条中的哪个节点可以处理你发出的请求,所以只需把请求传递给第一个节点就行。

如果在实际开发中,当维护一个含有多个条件分支语句的巨大函数时时,可以使用职责链模式。链中的节点对象可以灵活拆分重组,增加删除节点,且无需改动其他节点函数内的代码。

0 人点赞