美文网首页
职责链模式

职责链模式

作者: bby365 | 来源:发表于2018-07-06 10:13 被阅读0次

    处理请求的对象被连接成一条链,发送者不知道接收是谁,弱化两者之间的强联系。
    场景:乘坐公交车刷卡

    • 电商定金用户购买商品流程
    1. 一般代码
      使用if-else语句,缺点:分支过多,后期维护困难。
    2. 优化一
      将每一种情况,单独写成一个函数,如果处理不了,指定下一个处理函数去处理。
    var order500 = function( orderType, pay, stock ){
        if ( orderType === 1 && pay === true ){
            console.log( '500 元定金预购, 得到100 优惠券' );
        }else{
            order200( orderType, pay, stock ); // 将请求传递给200 元订单
        }
    
    };
    

    这样,每次都从order500开始,一直往下传递,直到被处理。
    缺点:每个函数都要指定特定的下一个处理函数,这样的话,顺序固定,太僵硬。

    1. 优化2
      每个处理函数,不再指定特定的处理函数,只是返回一个固定的标识符。
      然后创建一个构造函数chain ,具有2个原型方法:指定下一个节点 和 传递请求。
    // 1. 不会指定下一个节点,返回一个标识符,增加灵活性。
    var order500 = function( orderType, pay, stock ){
        if ( orderType === 1 && pay === true ){
            console.log( '500 元定金预购,得到100 优惠券' );
        }else{
            return 'nextSuccessor'; // 我不知道下一个节点是谁,反正把请求往后面传递
        }
    };
    
    // 2. 创建Chain构造函数,包括2个原型方法。
    // 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;
    };
    
    //  3-1 包装成职责链的节点
    var chainOrder500 = new Chain( order500 );
    var chainOrder200 = new Chain( order200 );
    var chainOrderNormal = new Chain( orderNormal );
    
    // 3-2 指定下一个节点
    chainOrder500.setNextSuccessor( chainOrder200 );
    chainOrder200.setNextSuccessor( chainOrderNormal );
    
    // 3-3 请求从第一个节点开始,处理不了,向后传递
    chainOrder500.passRequest( 3, true, 500 ); // 输出:普通购买,无优惠券
    

    优点:

    1. 可以自由的组合,指定请求传递顺序。
    2. 有新的需求,添加也很简单,调用setNextSuccessor()可以轻松插入到指定位置。
    • 异步职责链
      上面的例子,返回一个特定的值来把请求传递个下一个节点。但是有异步请求的情况下,需要等待返回结果。这就需要节点,有权利可以决定什么时候传递请求。

    模拟异步请求例子

     var fn1 = new Chain(function(){
         console.log(1)
         return 'nextSuccessor'
     })
     var fn2 = new Chain(function(){
         console.log(2)
         var self = this;
         setTimeout(function(){
            return 'nextSuccessor'
         },2000)
        
     })
     var fn3 = new Chain(function(){
         console.log(3)
     })
    fn1.setNextSuccessor(fn2).setNextSuccessor(fn3)
    console.log(fn1.passRequest())
    
    // 结果:1,2,undefined
    

    分析:

    1. fn2 中有个异步方法,fn2.passRequest()返回的是undefined ,异步方法中的返回值没有作用。
    2. 如果想要执行fn3,那么fn2中的异步方法要调用一个原型方法去将请求传递给fn3
    3. 上述的方法都放到原型上是因为这些方法是公用的,每一个节点都可能调用。

    改进:

    // 增加一个原型方法
    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()
         },2000)
        
     })
     var fn3 = new Chain(function(){
         console.log(3)
     })
    
    fn1.setNextSuccessor(fn2).setNextSuccessor(fn3)
    console.log(fn1.passRequest())
    

    相关文章

      网友评论

          本文标题:职责链模式

          本文链接:https://www.haomeiwen.com/subject/tldvuftx.html