JavaScript设计模式 职责链模式

一.生活场景

早高峰时,公交车上人实在太多了,经常上车后找不到售票员在哪,所以只好把两块钱硬币往前面递。除非你运气够好,站在你前面的第一个人就是售票员,否则,你的硬币通常要在 N 个人手上传递,才能最终到达售票员的手里。这种通过传递,最终传到售票员的模式就是职责链模式。

二.定义

职责链模式(Chain of Responsibility Pattern):使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系,将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。

请求发送者只需要知道链中的第一个节点,从而弱化了发送者和一组接收者之间的强联系。

三.实现预售优惠功能

现在我们做一个预售付定金,正式购买时返优惠券的功能。定金500对应100优惠券,200对应50优惠券,未付定金原价购买。
实现流程如下:

/***** orderType:预付类型  pay:是否付款  stock:商品库存 *****/
var order = function (orderType, pay, stock) {
    if (orderType === 1) { // 500 元定金购买模式
        if (pay === true) { // 已支付定金
            console.log('500 元定金预购, 得到 100 优惠券');
        } else { // 未支付定金,降级到普通购买模式
            if (stock > 0) { // 用于普通购买的手机还有库存
                console.log('普通购买, 无优惠券');
            } else {
                console.log('手机库存不足');
            }
        }
    }
    else if (orderType === 2) { // 200 元定金购买模式
        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('手机库存不足');
        }
    }
};
order(1, true, 500); // 输出: 500 元定金预购, 得到 100 优惠券

虽然实现了功能,但是太多的if判断失去了代码的阅读性,如果在增加定金类型会不断堆砌判断,我们也需要增加可维护性,利用职责链的思想给它解耦。

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 orderNormal = function (orderType, pay, stock) {
    if (stock > 0) {
        console.log('普通购买,无优惠券');
    } else {
        console.log('手机库存不足');
    }
};

// Chain.prototype.setNextSuccessor 指定在链中的下一个节点
// Chain.prototype.passRequest 传递请求给某个节点
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);
    }
    return ret;
};
//包装成职责链的节点
var chainOrder500 = new Chain(order500);
var chainOrder200 = new Chain(order200);
var chainOrderNormal = new Chain(orderNormal);
//指定节点在职责链中的顺序
chainOrder500.setNextSuccessor(chainOrder200);
chainOrder200.setNextSuccessor(chainOrderNormal);

chainOrder500.passRequest(1, true, 500); // 输出:500 元定金预购,得到 100 优惠券
chainOrder500.passRequest(2, true, 500); // 输出:200 元定金预购,得到 50 优惠券
chainOrder500.passRequest(3, true, 500); // 输出:普通购买,无优惠券
chainOrder500.passRequest(1, false, 0); // 输出:手机库存不足

如此我们解耦了不同预付方式的代码,如果再加入一个预付300返80优惠券类型,我们只需添加一个函数,放入职责链中执行。

var order300 = function (orderType, pay, stock) {
    if (orderType === 1 && pay === true) {
        console.log('300 元定金预购,得到 80 优惠券');
    } else {
        return 'nextSuccessor'; // 我不知道下一个节点是谁,反正把请求往后面传递
    }
}; 
chainOrder300= new Chain( order300 );
chainOrder500.setNextSuccessor( chainOrder300);
chainOrder300.setNextSuccessor( chainOrder200); 

四.异步的职责链

上面的例子中我们模拟的是同步事件,开发中我们也会遇到一些异步的问题,比如ajax请求等等。我们模拟一个职责链异步实现。

var fn1 = new Chain(function () {
    console.log(1);
    return 'nextSuccessor';
});
var fn2 = new Chain(function () {
    console.log(2);
    var self = this;
    setTimeout(function () {
        self.next();
    }, 1000);
});
var fn3 = new Chain(function () {
    console.log(3);
});
fn1.setNextSuccessor(fn2).setNextSuccessor(fn3);
fn1.passRequest(); 

现在我们得到了一个特殊的链条,请求在链中的节点里传递,但节点有权利决定什么时候把请求交给下一个节点。

五.总结

职责链模式可以很好地帮助我们管理代码,降低发起请求的对象和处理请求的对象之间的耦合性。职责链中的节点数量和顺序是可以自由变化的,我们可以在运行时决定链中包含哪些节点。无论是作用域链、原型链,还是 DOM 节点中的事件冒泡,我们都能从中找到职责链模式的影子。职责链模式还可以和组合模式结合在一起,用来连接部件和父部件,或是提高组合对象的效率。

你可能感兴趣的:(JavaScript设计模式,js,设计模式)