Fork me on GitHub

职责链模式


使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系,将这些对象都练成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。

如图所示:
请求→A→B→C→D
再来一个更形象的比喻,读书的时候假设你坐在前面,要在上课时告诉后排一些事情,这时候你可能会选择写一张小纸条,小纸条就会陆续的被向后传递。
从上面的例子中,我们可以看到职责链的优点:请求发送者只需要知道链中的第一个节点,从而弱化了发送者和一组接收者之间的强联系。

实际开发中的职责链模式

需求:一个售卖手机的电商网站,交纳500定金和200定金和没交定金的有不同的优惠状况。
我们刚开始可能会这么写:

// orderType: 订单类型(普通用户或定金用户),1为500元定金用户,2为200,3为普通用户
// pay:表示是否已支付定金,true或false
// 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) {
        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);

以上代码虽然保证了可以用的状态,但是只是能用,如果要修改就会很麻烦,而且阅读体验也不是很好,所以我们做一点优化

var order500 = function (orderType, pay, stock) {
    if (orderType === 1 && pay === true) { //500元定金购买模式
            console.log('500元定金预定,已获得100元优惠券');
    } else { 
        order200 (orderType, pay, stock); //将请求传递给200元订单
    }
};

var order200 = function (orderType, pay, stock) {
    if (orderType === 2 && pay === true) { //200元定金购买模式
            console.log('200元定金预定,已获得50元优惠券');
    } else { 
        orderNormal (orderType, pay, stock); //将请求传递给普通订单
    }
};
var orderNormal =  function (orderType, pay, stock) {
    if (stock > 0) {
        console.log('普通购买,无优惠券');
    } else {
        console.log('库存不足');
    }
};

order500 (1, true, 500);

虽然这有了小小进步,不过请求在链条中的传递依然很僵硬,传递请求的代码被耦合在了业务函数中,这显然是违反开放-封闭原则的,例如如果有一天我们要增加300元预定或者200元预定,意味着就必须改动这些业务函数的内部,就像一根根环环相扣的死结链条。

灵活可拆分的职责链

var order500 = function (orderType, pay, stock) {
    if (orderType === 1 && pay === true) { //500元定金购买模式
            console.log('500元定金预定,已获得100元优惠券');
    } else { 
        return 'nextSuccessor'; //我不知道下一个节点是谁,反正向后传递就好了
    }
};

var order200 = function (orderType, pay, stock) {
    if (orderType === 2 && pay === true) { //200元定金购买模式
            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(successor) {
    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.passRequest(1, true, 500);  //500定金优惠100
chainOrder200.passRequest(2, true, 500); //200定金优惠50
chainOrderNormal.passRequest(3, true, 500); //普通购买无优惠券
chainOrderNormal.passRequest(1, false, 0); //库存不足
我知道是不会有人点的,但万一有人想不开呢?