JavaScript设计模式十一(职责链模式)

JavaScript设计模式十一(职责链模式)

定义:

职责链模式的定义是使多个对象都有机会处理请求,从而避免请求的发送者和接受者之间的耦合关系,把这些对象连成一条链,并沿着这条链传递改请求,直到有一个对象能够处理它为止。

我们看个图就可以了
这里写图片描述

现实生活中的职责链模式

显示生活中其实有很多关于职责链模式的例子

  • 我们现在上海的公交车上有些还是有售票员的,但是如果是早高峰,售票员也走不动,这种情况一般我们是让别人递过去,运气好,第一个人就是售票员,否则我们的公交卡要传递很多次才到售票员手中
  • 例如我们在食堂买面,食堂有汤面和拌面,第一个人要汤面,但是现在是一碗拌面,所以就看后面又没人要,需要就来前面来刷卡

职责链模式中最大的有点在于:请求发送者只需要知道链中的第一个节点,从而弱化了发送者和一组接受者之间的强联系,如果不使用职责链模式,那么我们就要搞清楚谁是售票员,才能把硬币给他。

实际开发中的职责链模式

我们看一个实际开发中的例子:
假如我们负责一个卖手机的网站,经过分别交纳500元定金和200元定金的两轮预定之后,现在到了正是购买的阶段。
公司对于前面预定的用户有一定的优惠政策,支付过500元定金的可以收到100元的优惠券,200元定金的用户可以收到50元的优惠券,而没有预定的用户没有优惠券,并且在库存有限的情况下还不一定能买到。
我们约定后台返回的字段:

  • orderType 订单类型,1表示500元的定金用户;2表示200元的定金用户;3表示普通用户
  • pay 用户是否付了定金,如果用户下了订单,没付还是普通用户
  • stock 手机库存限制,普通用户会有限制,但是付了定金的用户不受库存限制
var order = function(orderType, pay, stock) {
    if (orderType === 1) {
        if (pay === true) {
            console.log('支付了500定金,有100的优惠券');
        } else {
            if (stock > 0) {
                console.log('普通购买,无优惠券');
            } else {
                console.log('手机库存不够');
            }
        }
    } else if (orderType === 2) {
            if (pay === true) {
            console.log('支付了200定金,有50的优惠券');
        } else {
            if (stock > 0) {
                console.log('普通购买,无优惠券');
            } else {
                console.log('手机库存不够');
            }
        }
    } else if (orderType === 3) {
            if (stock > 0) {
                console.log('普通购买,无优惠券');
            } else {
                console.log('手机库存不够');
            }
    }
}

一般我们代码都是这样的,但是这段代码后续的维护会很痛苦,我们用职责链模式来重构代码:

// 500元订单
var order500 = function(orderType, pay, stock) {
    if (orderType === 1 && pay === true) {
        console.log('500元定金预购,100元优惠券');
    } else {
        order200(orderType, pay, stock);
    }
}

// 200元订单

var order200 = function(orderType, pay, stock) {
    if (orderType === 2 && pay == true) {
        console.log('200元定金预购,50元优惠券');
    } else {
        orderNormal(orderType, pay, stock);
    }
}

// normal 
var orderNormal = function (orderType, pay, stock) {
    if (stock > 0) {
        console.log('普通购买,无优惠券');
    } else {
        console.log('库存不够');
    }
}

我们这一步已经有了很大的进步,把前面那个巨大的order函数拆分为了3个函数,去掉了很多嵌套的if-else语句,但是还有点问题,我们的请求在这个链接中传递的很僵硬,并且请求传递的代码被耦合在了业务函数里面。上面order500和order200就耦合在一起了,这是违反了开放-闭合原则的,比如某天我们业务逻辑变化了,需要增加300元的预定们就要修改order500函数了,这样这个链条不够灵活,我们看看如何弄成灵活的职责链:

我们约定,如果某个节点处理不了,就返回一个特定的字符串’nextSuccessor’

var order500 = function(orderType, pay, stock) {
    if (orderType === 1 && pay === true) {
        console.log('500元定金预购,得到100元优惠券');
    } else {
        return 'nextSuccessor';
    }
}

var order200 = function(orderType, pay, stock) {
    if (orderType === 2 && pay === true) {
        console.log('200元定金预购,得到50元优惠券');
    } else {
        return 'nextSuccessor';
    }
}

var orderNomal = function(orderType, pay, stock ) {
    if (stock > 0) {
        console.log('普通购买');
    } else {
        console.log('库存不够了');
    }
}

var Chain = function(fn) {
    this.fn = fn;
    this.successor = null;
}

Chain.prototype.setNextSuccessor = function(successor) {
    return this.successor = successor;
};

Chain.prototype.passRequest = function() {
    var ret = this.fn.apply(this, arguments);
    if (ret === 'nextSuccessor') {
        return this.successor && this.successor.passRequest.apply(this.successor, arguments);
        // 这里apply第一个参数为啥是this.successor, 测试发现这里填this,或者null会怎么样呢?
    }
    return ret;
}

var chainOrder500 = new Chain(order500);
var chainOrder200 = new Chain(order200);
var chainOrderNormal = new Chain(orderNomal);

chainOrder500.setNextSuccessor(chainOrder200);
chainOrder200.setNextSuccessor(chainOrderNormal);

// 然后把请求给第一个节点
chainOrder500.passRequest(1, true, 500);

上面的疑问,如果填入this,表示调用了Chain.passRequest,会一直循环调用,如果传入null,null对象没有passRequest方法,而this.successor就是我们之前约定的chainOrder200或者chainOrderNormal

后续如果增加了chainOrder300,我们只需要增加一个300的函数,然后把链调整一下就可以了。

职责链的优缺点

优点:

  • 解耦了请求发送者和N个接受者之间的复杂关系
  • 使用了职责链模式之后,链中的节点对象可以灵活的拆分重组
  • 我们可以手动的指定开始的节点,如果我们明确知道第一个不是

缺点:

  • 我们不能保证某个请求一定会被链中节点处理,解决方法是我们可以在链尾增加一个保底的请求
  • 职责链模式增加了一些节点,大部分节点是没有实质的作用的,只是传递的作用,从性能上考虑,我们要避免职责链过长导致的性能问题

用AOP实现职责链

我们可以利用JavaScript函数式的特性,利用AOP来实现职责链

Function.prototype.after = function(fn) {
    var self = this;
    return function() {
        var ret = self.apply(this, arguments);
        if (ret === 'nextSuccessor') {
            return fn.apply(this, arguments);
        }
        return ret;
    }
}

var order = order500.after(order200).after(orderNormal);

order(1, true, 500);

小结

其实大家想想,在作用域链、原型链中还是DOM节点的事件冒泡中,我们都可以看到职责链的影子。职责链模式还可以和组合模式一起使用,用来联系子组件和父组件,或则提高组合对象的效率。

猜你喜欢

转载自blog.csdn.net/lihangxiaoji/article/details/80179066
今日推荐