美文网首页
第十五章装饰者模式

第十五章装饰者模式

作者: 狐尼克朱迪 | 来源:发表于2016-09-29 10:33 被阅读0次

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

    实现

    一个例子,我们希望在原有的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图片是一个聪明的做法。
    装饰者模式是为对象动态的加入行为,用于不能确定本体对象全部功能的情况下,因此有可能形成一条长长的装饰链。

    相关文章

      网友评论

          本文标题:第十五章装饰者模式

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