设计模式——代理模式

190 阅读3分钟

代理模式是为一个对象提供一个代用品或占位符,以便控制对它的访问。

代理模式的关键是,当客户不方便直接访问一个对象或者不满足需要的时候,提供一个替身对象来控制对这个对象的访问,客户实际上访问的是替身对象,替身对象对请求做出一些处理之后,再把请求转给本体对象。

代理模式的例子

我们实现一个小明追MM的例子:

var Flower = function(){};
var xiaoming = {
    sendFlower:function(target){
        var flower = new Flower();
        target.receiveFlower(flower);
    }
};
var B = {
    receiveFlower: function(flower){
        A.receiveFlower(flower);
    }
};
var A = {
    receiveFlower: function(flower){
        console.log('收到花:' + flower)
    }
};

通过上面的代码我们实现了一个最简单的代理模式。从表面上看起来,明明小明可以直接给A送花为啥要通过代理B。确实在这个例子中代理模式毫无用处,但是如果我们给A增加一个背景设定,假设A在心情好的时候收到花的概率为60%,心情差的时候收到花的概率基本为0,并且花的成本很高,这样实现的例子如下:

var Flower = new function(){};
var xiaoming = {
    sendFlower:function(target){
        var flower = new Flower();
        target.receiveFlower(flvarower);
    }
};
var B = {
    receiveFlower: function(flower){
        A.listenGoodMood(function(){
            A.receiveFlower(flower); //监听A的好心情
        });
    }
};
var A = {
    receiveFlower:function(flower){
        console.log('收到花:' + flower)
    }
    listenGoodMood:function(fn){
        setTimeout(function(){ //假设10秒后A的心情变好
            fn();
        },10000)
    }
}
保护代理和虚拟代理

代理B可以帮助代理A过滤掉一些请求,比如送花的人中年龄太大或者长相太丑,这种请求可以直接在代理B处被拒绝掉。这种代理叫做保护代理。 假设现实中的话价格不菲,导致在程序世界中,new Flower()是一个代价昂贵的操作,那么我们可以把new Flower的操作交给代理B去执行,代理B会选择在A心情好的时候在执行new Flower()操作,这是代理的两一种形式虚拟代理。

虚拟代理实现图片的预加载

图片加载过程中,我们常见的做法先用一张loading图片占位,然后用异步的方式加载图片,等图片加载好了再把它填充到img节点里,这种场景就很适合使用虚拟代理。

var myImage = (function(){
    var imgNode = document.createElement('img');
    document.body.appendChild(imgNode);
    return {
        setSrc:function(src){
            imgNode.src = src;
        }
    }
})();
var porxyImage = (function(){
    var img = new Image();
    img.load = function(){
        myImage.setSrc(this.src)
    }
    return {
        setSrc: function(src){
            myImage.setSrc('file:// /C:/loading.gif');
            img.src =src;
        }
    }
})();

proxyImage.setSrc('http://xxxxx.jpg');
虚拟代理合并HTTP请求

我们做一个文件同步的功能,当我们选中一个checkbox的时候,它对应的文件就会被同步到另一台备用服务器上面:

var synchronousfile = function(id){
    console.log('开始同步文件,id为:' + id);
}
var proxySynchronousFile = (function(){
    var chache = [],timer;
    return function(id){
        cache.push(id);
        if(timer){  // 保证不会覆盖已启动的定时器
            return;
        }
        timer = setTimeOut(function(){
            synchronousfile(cache.join(','));
            clearTimeout(timer);
            timer = null;
            cache.length = 0; 
        },2000);
    }
})();
var checkbox = document.getElementsByTagName('input');
for(var i = 0,c;c=checkbox[i++];){
    c.onclick = function(){
        if(this.checked === true){
            porxySynchronousFile(this.id);
        }
    }
}

通过一个代理函数porxySynchronousFile 收集2s内的请求,最后一次性发送给服务器。

缓存代理了
var mult = function(){
    var a = 1;
    for(var i=01=arguments.length;i<1,i++){
        a = a* arguments[i];
    }
    return a;
}
//代理缓存
var proxyMult = (function(){
    var cache = {};
    return function(){
        var args = Array.prototype.join.call(arguments,',');
        if(args in cache){
            return cache[args];
        }
        return cache[args] = mult.apply(this.arguments);
    }
})()
proxyMult(1,2,3,4); //24
proxyMult(1,2,3,4); //缓存中取出 24