前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >javascript设计模式八:职责链模式

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

作者头像
前端_AWhile
发布2019-08-29 13:14:58
2820
发布2019-08-29 13:14:58
举报
文章被收录于专栏:前端一会

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

需求背景: 一个电商网站,用户交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个接收者之间的复杂关系,由于不知道链条中的哪个节点可以处理你发出的请求,所以只需把请求传递给第一个节点就行。

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

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2018-11-06,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 前端小二 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档