职责链模式


一、定义

职责链模式的定义是:使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间 的耦合关系,将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。 示意图如图1所示。
在这里插入图片描述

图1 职责链模式

二、实现

问题背景。
假设我们负责一个售卖手机的电商网站, 经过分别交纳 500 元定金和 200 元定金的两轮预定 后(订单已在此时生成) ,现在已经到了正式购买的阶段。 公司针对支付过定金的用户有一定的优惠政策。在正式购买后,已经支付过 500 元定金的用 户会收到 100 元的商城优惠券, 200 元定金的用户可以收到 50 元的优惠券, 而之前没有支付定金 的用户只能进入普通购买模式,也就是没有优惠券,且在库存有限的情况下不一定保证能买到。
我们的订单页面是 PHP 吐出的模板,在页面加载之初,PHP会传递给页面几个字段。

  • orderType:表示订单类型(定金用户或者普通购买用户) ,code 的值为 1 的时候是 500 元 定金用户,为 2 的时候是 200 元定金用户,为 3 的时候是普通购买用户。
  • pay:表示用户是否已经支付定金,值为 true 或者 false, 虽然用户已经下过 500 元定金的 订单,但如果他一直没有支付定金,现在只能降级进入普通购买模式。
  • stock:表示当前用于普通购买的手机库存数量,已经支付过 500 元或者 200 元定金的用 户不受此限制。

2.1 简单实现

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

};
// 200元订单
var order200 = function (orderType, pay, stock) {
    if (orderType === 2 && pay === true) {
        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);    // 输出:500元定金预购, 得到100优惠券 
order500(1, false, 500);   // 输出:普通购买, 无优惠券 
order500(2, true, 500);    // 输出:200元定金预购, 得到500优惠券 
order500(3, false, 500);   // 输出:普通购买, 无优惠券 
order500(3, false, 0);     // 输出:手机库存不足 

2.2 使用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 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('手机库存不足'); }
}; 

var order = order500.after(order200).after(orderNormal);
order(1, true, 500);    // 输出:500元定金预购,得到100优惠券 
order(2, true, 500);    // 输出:200元定金预购,得到50优惠券 
order(1, false, 500);   // 输出:普通购买,无优惠券 

三、具体应用

我们前端页面的表单中,往往有很多校验。我们可以把这些校验连起来。比如我们现在需要对一个控件A进行两个校验(A1,A2)。我们可以对A先进行A1校验,如果A1校验通过了我们就传递给A2校验,这样当A1校验不通过时,我们就可以避免使用A2进行多余的校验。

/*职责链模式,便于参数校验,ok说明校验通过(当然,返回的参数是你自己定义的,你想定义为其他参数也行)*/
Function.prototype.after = function (fn) {
    var self = this;
    return function () {
        var ret = self.apply(this, arguments);
        //ok就向下校验
        if (ret === 'ok') {
            return fn.apply(this, arguments);
        }
        return ret;
    }
}
function verifyCac516(obj) {
    /*obj不能为空*/
    function v1() {

        if (isEmpty(obj.value)) {
            Base.alert("档案出生日期为空", 'warn', function () {
                Base.focus('cac516');
            });
            return 'error';
        }
        return 'ok';
    }

    /*性别不能为空*/
    function v2() {

        var gender = Base.getValue('aac004');
        if (isEmpty(gender)) {
            Base.alert("性别为空", 'warn', function () {
                Base.focus('aac004');
            });
            return 'error';
        }
        return 'ok';
    }

    /*选择日期后计算其年龄并判断是否小于退休年龄并给予警告提示:
    该人员年龄未达到退休年龄,请谨慎操作!提示后可以继续往下操作。*/
    function v3(obj) {

        var gender = Base.getValue('aac004');
        var age = ages(obj.value);
        if (gender === "1") {
            if (age < txnlM) {
                Base.alert("该人员年龄未达到退休年龄,请谨慎操作!")
            }
        } else if (gender === '2') {
            if (age < txnlF) {
                Base.alert("该人员年龄未达到退休年龄,请谨慎操作!");
            }
        }
    }

    var verify = v1.after(v2).after(v3);
    verify(obj);
}

上面代码中,具体逻辑可以不用知道。只需要知道verifyCac516obj.value进行了三个校验v1,v2,v3,若其中任何一个校验失败了,就可以不用交个后面的校验函数进行校验了。

四、总结

优点。

  • 解耦了请求发送者和 N 个接收者之间的复杂关 系,由于不知道链中的哪个节点可以处理你发出的请求,所以你只需把请求传递给第一个节点即可。
  • 链中的节点对象可以灵活地拆分重组。增加或者删除一个节 点,或者改变节点在链中的位置都是轻而易举的事情。
  • 可以手动指定起始节点,请求并不是非得从链中的第一个 节点开始传递。

缺点。

  • 不能保证某个请求一定会被链中的节点处理。
  • 职责链模式使得程序中多了一些节点对象,可能在某一次的请求传递过程中,大部分 节点并没有起到实质性的作用,它们的作用仅仅是让请求传递下去,从性能方面考虑,我们要避 免过长的职责链带来的性能损耗。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值