设计模式之职责链模式

Posted by franki on November 8, 2018

responsibilityline

职责连模式

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

职责链模式的名字非常形象,一系列可能会处理请求的对象被连接成一条链,请求在这些对象之间依次传递,直到遇到一个可以处理它的对象,我们把这些对象称为链中的节点。

// 订单处理

// 500元订单 orderType 订购类型 pay是否已经支付 stock是否还有库存
var order500 = function(orderType, pay, stock) {
    if (orderType === 1 && pay === true) {
        console.log("500元定金预购,得到100元优惠券");
    } else {
        return "nextSuccessor"; // 我不知道下一个节点是谁,反正把请求往后面传递
    }
};

// 200元订单
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("手机库存不足");
    }
};

// 把上面的订单函数全部封装为一个包装函数传入到职责链对象中
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 order300 = function(orderType, pay, stock) {
    if (orderType === 3 && pay === true) {
        console.log("300定金预购,得到70元优惠");
    } else {
        return "nextSuccessor";
    }
};

// 现在3个订单函数包装为职责链的节点
var chainOrder500 = new Chain(order500);
var chainOrder300 = new Chain(order300);
var chainOrder200 = new Chain(order200);
var chainOrderNormal = new Chain(orderNormal);

// 然后指定节点在职责链中的顺序
chainOrder500.setNextSuccessor(chainOrder300);
chainOrder300.setNextSuccessor(chainOrder200);
chainOrder200.setNextSuccessor(chainOrderNormal);

// 最后把请求节点传递给第一个节点
chainOrder500.passRequest(1, true, 500);
chainOrder500.passRequest(2, true, 500);
chainOrder500.passRequest(3, true, 500);
chainOrder500.passRequest(1, false, 0);

我们总是喜欢去改动相对容易的地方,就像改动框架的配置文件远比改动框架的源代码简单得多。

接着处理职责链仲的异步

// 职责链中异步操作
Chain.prototype.next = function() {
    return this.successor && this.successor.passRequest.apply(this.successor, arguments);
};

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();

小小总结下,职责链的优缺点,优点是发送者与多个接收者之间的复杂关系,由于不知道链仲的哪个节点可以处理你发出的请求,所以你只需要把请求传递给第一个节点即可。可以把一个充斥着巨大无比的if else函数,只需要维护单独的函数代码,其次使用了职责链后,可以很方便的对链中的节点对象进行拆分重组,还可以手动指定起始节点。

缺点是,如果职责链中的每个节点对象都不能解决你发出的请求的,在这种情况下,我们可以在链尾增加一个保底的接收者节点来处理这种即将离开链尾的请求。

// 用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(order300).after(order200).after(orderNormal);
order(1, true, 300);
order(2, true, 300);
order(3, true, 300);
order(1, false, 300);