场景
假设我们负责一个售卖手机的电商网站,经过分别交纳 500 元定金和 200 元定金的两轮预定 后(订单已在此时生成),现在已经到了正式购买的阶段。 公司针对支付过定金的用户有一定的优惠政策。在正式购买后,已经支付过 500 元定金的用 户会收到 100 元的商城优惠券,200 元定金的用户可以收到 50 元的优惠券,而之前没有支付定金 的用户只能进入普通购买模式,也就是没有优惠券,且在库存有限的情况下不一定保证能买到。
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 优惠券
用职责链模式重构代码
先把 500 元订单、200 元订单以及普通购买分成 3 个函数。 接下来把 orderType、pay、stock 这 3 个字段当作参数传递给 500 元订单函数,如果该函数不 符合处理条件,则把这个请求传递给后面的 200 元订单函数,如果 200 元订单函数依然不能处理 该请求,则继续传递请求给普通购买函数,代码如下:
// 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(srock > 0){console.log('common buyer , no discount')}else {console.log('stock is not enough')}}// test resultorder500( 1 , true, 500); // 输出:500 元定金预购, 得到 100 优惠券order500( 1, false, 500 ); // 输出:普通购买, 无优惠券order500( 2, true, 500 ); // 输出:200 元定金预购, 得到 500 优惠券order500( 3, false, 500 ); // 输出:普通购买, 无优惠券order500( 3, false, 0 ); // 输出:手机库存不足
可以看到,执行结果和前面那个巨大的 order 函数完全一样,但是代码的结构已经清晰了很 多,我们把一个大函数拆分了 3 个小函数,去掉了许多嵌套的条件分支语句。 目前已经有了不小的进步,但我们不会满足于此,虽然已经把大函数拆分成了互不影响的 3 个小函数,但可以看到,请求在链条传递中的顺序非常僵硬,传递请求的代码被耦合在了业务函 数之中:
var order500 = function( orderType, pay, stock ){if ( orderType === 1 && pay === true ){console.log( '500 元定金预购, 得到 100 优惠券' );}else{order200( orderType, pay, stock );// order200 和 order500 耦合在一起}};
灵活可拆分的职责链节点
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( '手机库存不足' );}}// 定义构造函数 Chainvar Chain = function(fn){this.fn = fnthis.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}// 现在我们把 3 个订单函数分别包装成职责链的节点:var chainOrder500 = new Chain(order500)var chainOrder200 = new Chain(order200)var chainOrderNormal = new Chain(orderNormal)// 指定节点在职责链中的顺序chainOrder500.setNextSuccessor(chainOrder200)chainOrder200.setNextSuccessor(chainNormal)// 把请求传递给第一个节点chainOrder500.passRequest( 1, true, 500 ); // 输出:500 元定金预购,得到 100 优惠券chainOrder500.passRequest( 2, true, 500 ); // 输出:200 元定金预购,得到 50 优惠券chainOrder500.passRequest( 3, true, 500 ); // 输出:普通购买,无优惠券chainOrder500.passRequest( 1, false, 0 ); // 输出:手机库存不足
思考
对于常用的if esle 来说,虽然简单,但是对与复杂条件需要写各种判断逻辑,造成代码冗余。类似于策略模式,之职责链模式,先对要执行的操作进程单独封装,然后根据业务逻辑组织程一条链,从而实现任务从上而下去执行,后期如果有其它需求 ,则代码的修改成本较小
异步的职责链
让每个节点函数同步返回一个特定的值”nextSuccessor”,来表示 是否把请求传递给下一个节点。而在现实开发中,我们经常会遇到一些异步的问题,比如我们要在 节点函数中发起一个 ajax异步请求,异步请求返回的结果才能决定是否继续在职责链中 passRequest。 这时候让节点函数同步返回”nextSuccessor”已经没有意义了,所以要给 Chain 类再增加一个原型方法 Chain.prototype.next,表示手动传递请求给职责链中的下一个节点:
Chain.protoType.next = function(){return this.successor && this.successor.passRequest.apply(this.successor,arguments)}
