第十五章装饰者模式

在程序开发时,我们不希望某个类或者函数的方法非常复杂,一次就包含很多职责;那么我们可以采用装饰者模式,它可以在不改变原生对象的基础上,动态地给某个对象添加一些额外的方法或者属性,使其满足更复杂的用户需求。

实现

一个例子,我们希望在原有的onload基础上添加一个方法,而我们不想改变原有的onload方法(或者方法太复杂,总之我们保持器神秘性),那么我们可以这么做:

    window.onload = funciton(){// 不知道的逻辑  ***}
    function my(){alert(2);} // 我自己的逻辑
    
    var _onload = window.onload || function(){};
    window.onload = funciton(){
        _onload();
        my();
    }

上面这样的做法有可能会产生一个问题,那就是作用域的不延续(也就是this被劫持的问题),比如:

    var _getElementById = document.getElementById;
    document.getElementById = function(id){
        alert(1);
        return _getElementById( id ); // 调用的时候会抛出异常,因为getElementById需要document作用域。
    }

为了解决这类问题,同时提出一个比较适用的装饰者解决方案,我们借组AOP进行实现:

  // 实现1  本文都采用此方法
Function.prototype.before = function( fn ){
    var _self = this;
    return function(){
        fn.apply(this, arguments); 
        _self.apply(this, arguments);
    }
}
    
Function.prototype.after = function( fn ){
    var _self = this;
    return function(){
        _self.apply(this, arguments);
        fn.apply(this, arguments); 
    }
}

// 实现2
Function.prototype._$aop = function(_before,_after){
    var f = function(){},
        _after = _after||f,
        _before = _before||f,
        _handler = this;
    return function(){
        var _event = {args:[].slice.call(arguments,0)};
        _before(_event);
        if (!_event.stopped){
            _event.value = _handler
                  .apply(this,_event.args);
            _after(_event);
        } 
        return _event.value;
    };
};

那么可以把上面的方法进行如下装饰:

    document.getElementById = document.getElementById.before(function(){
        alert(1);
    })
实用

几个用到AOP的地方:

  • 数据统计: 如果我们要统计事件,假设点击出现登录框时要发送统计请求,可以采用如下所示的方法:

      var showLogin = function(){
          // open the dialog
      }
      var log = function(){
          // 上报
      }
      showLogin = showLogin.after(log);
    
  • 改变函数参数: 当我们网站受到攻击时,需要在ajax请求中加上一个token参数,如下:

      // 原来的ajax
      var ajax = funciton( type, url, param ){// ajax逻辑}
          
      // 修改后的
      var ajax = function( type, url, param ){
          param = param || {};
          param.token = getToken();
          ajax(type, url, param);
          // ajax逻辑
      }
          
      // 如果我们又不想改变原来的ajax库(有可能以后新的项目需要用到,互联网总是很多的新项目..)
      ajax = ajax.before(function(type, url, param){
          param.token = getToken(); 
      });
    
  • 校验需求: 提交表单时,校验不通过,直接返回。

    Function.prototype.before = function( fn ){
       var _self = this;
       return function(){
         if (fn.apply(this, arguments) === false){
             return;
         } 
         _self.apply(this, arguments);
       }
     }
    
     function formSumbit(){
         // ajax提交
     }
     function validate(){
         if( user.name === ''){
             alert("名字为空");
            return false;
         }
         ...
     }
     formSumbit = formSumbit.before(validate);
    
装饰者模式和代理模式的区别

代理模式的目的是,当直接访问本体不方便或者不符合需求时,为这个本体提供一个代替者。本体定义了关键功能,而代理提供或拒绝对它的访问,或者在访问本体前做一些额外的事情。 换句话说,代理模式强调一种关系(Proxy与它的实体之间的关系),这种关系一开始就可以被确定。以图片加载为例,为图片设置src时最终目的,而在之前设置一个loading图片是一个聪明的做法。
装饰者模式是为对象动态的加入行为,用于不能确定本体对象全部功能的情况下,因此有可能形成一条长长的装饰链。

你可能感兴趣的:(第十五章装饰者模式)