第6章代理模式

第6章代理模式

6.1 第一个例子--小明追MM的故事

var Flower = function () {};

var xiaoming = {
    sendFlower : function(target){
        var flower = new Flower();
        target.receiveFlower(flower);
    }
}

var A = {
    receiveFlower:function(flower){
        console.log('收到花'+flower);
    }
}

xiaoming.sendFlower(A);

引入代理B

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);
    }
}

xiaoming.sendFlower(B);

B会监听A的心情变化

var Flower = function () {};

var xiaoming = {
    sendFlower : function(target){
        var flower = new Flower();
        target.receiveFlower(flower);
    }
}

var B = {
    receiveFlower:function(flower){
        A.listenGoodMood(function(){
            A.receiveFlower(flower)
        });
    }
}
var A = {
    receiveFlower:function(flower){
        console.log('收到花'+flower);
    },
    listenGoodMood:function(fn){
        setTimeout(function(){
            fn();
        },3000);

    }
}

xiaoming.sendFlower(B);

6.2 保护代理和虚拟代理

比如送花的人中年龄太大或者没有宝马,这种请求就可以直接在代理B处被拒绝掉,这种代理叫保护代理。

白脸A继续保持良好的女神形象,不希望直接拒绝任何人,于是找了黑脸B来控制对A的访问

代理B会在A心情好时再执行new Flower,这叫虚拟代理,它会把一些开销很大的对象,延迟到真正需要它的时候才去创建

var B = {
    receiveFlower:function(){
        A.listenGoodMood(function(){
            var flower = new flower();
            A.receiveFlower(flower);
        });
    }
}

在Javascript中并不容易实现保护代理,因为我们无法判断谁访问了某个对象。而虚拟代理是最常用的一种代理模式,本章主要讨论的也是虚拟代理。

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

var myImage = (function(){
    // body...
    var imgNode = document.createElement('img');
    document.body.appendChild(imgNode);

    return {
        setSrc : function(src){
            imgNode.src = src;
        }
    }
})();

var proxyImage = (function(){
    var img = new Image;
    img.load = function(){
        myImage.setSrc(this.src);
    }
    return {
        setSrc:function(src){
            myImage.setSrc('file://jdjisj.gif');
            img.src = src;
        }
    }

})()

myImage.setSrc('http:wwjjfjds.jpg');

6.4 代理的意义

不用代理的预加载图片函数实现

var myImage = (function(){
    var imgNode = document.createElement('img');
    document.body.appendChild(imgNode);
    var img = new Image;

    img.onload = function(){
        imgNode.src = img.src;
    };

    return {
        setSrc:function(){
            imgNode.src = 'file://dsd.gif';
            img.src = src;
        }
    }
})()

MyImage.setSrc('http://dsadsd.jpg');

面向对象设计的原则--单一职责原则

面向对象设计鼓励将行为分布到细粒度的对象之中,如果一个对象承担的职责过多,等于把这些职责耦合到了一起,这中耦合会导致脆弱和低内聚的设计。当发生变化时,设计可能会遭到意外的破环。

面向对象程序中,大多数情况下,若违反其他任何原则,同时将违反开放-封闭原则。这时删预加载代码就不得不动MyImage对象了

代理负责预加载图片完成后把请求重新交给本体。

不需要预加载了只需要改成请求本体而不是请求代理对象即可

6.5 代理和本体接口的一致性

代理对象和本体都对外提供了setSrc方法,在客户看来,代理对象和本体是一致的,代理接手请求的过程对于用户来说是透明的,用户并不清楚代理和本体的区别。

  • 用户可以放心的请求代理,他只关心是否能得到想要的结果

  • 在任何使用本体的地方都可以替换成使用代理

在Javascript这种语言中,我们有时通过鸭子类型来检测代理和本体是否都实现了setSrc方法,另外大多数时候甚至不做检测,全部依赖程序员的自觉性

没有接口的世界

如果代理对象和本体对象都为一个函数(函数也是对象),函数必然都能被执行,则可以认为它们也具有一致的“接口”

var myImage = (function(){
    var imgNode = document.createElement('img');
    document.body.appendChild(imgNode);

    return function(src){
        imgNode.src = src;
    }
})();

var proxyImage = (function(){
    var img = new Image;
    img.onload = function(){
        myImage(this.src);
    }

    return function(src){
        myImage('file:fsfdf.gif');
        img.src = src;
    }
})();

proxyImage('http://sds.jpg');

6.6 虚拟代理合并HTTP请求




    
    Document


1
2
3
4
5
6
7
8
9




如此频繁的网络请求将会带来相当大的开销

var synchronousFile = function(id){
    console.log('开始同步文件,id为:' + id);
}

var proxySynchronousFile = (function(){
    var cache = [],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) {
            proxySynchronousFile(this.id);
        }
    }
}

6.7 虚拟代理在惰性加载中的应用

var miniConsole = (function(){
    var cache = [];
    var handler = function(ev){
        if(ev.keyCode === 113){
            var script = document.createElement('script');
            script.onload = function(){
                for(var i = 0, fn;fn = cache[i++];){
                    fn();
                }
            }
            script.src = 'miniConsole.js';
            document.getElementsByTagName('head')[0].appendChild(script);
            document.body.removeEventListener('keydown',handler);//只加载一次miniConsole.js
        }
    };
    document.body.addEventListener('keydown',handler,false);
    return {
        log:function(){
            var args = arguments;
            cache.push(function(){
                return miniConsole.log.apply(miniConsole,args);
            });
        }
    }
})();

miniConsole.log(11);

//miniConsole.js代码
miniConsole = {
    log:function(){
        //真正代码略
        console.log(Array.prototype.join.call(arguments));
    }
}

6.8 缓存代理

6.8.1 缓存代理的例子--计算乘积

var mult = function(){
    console.log('开始计算');
    var a = 1;
    for(var i=0, l = arguments.length;i

6.8.2 缓存代理用于ajax异步请求数据

已经拉取的数据在某个地方被缓存之后,下次再请求同一页的时候,便可以直接使用之前的数据。

这里也可以引入缓存代理,实现方式跟计算乘积的例子差不多,唯一不同的是,请求是个异步操作,我们无法把 计算结果放在代理对象的缓存中,而是通过回调的方式

6.9 用高阶函数动态创建代理

var mult = function(){
    var a = 1;
    for(var i = 0,l = arguments.length;i

6.10 其他代理模式

防火墙模式:控制网络资源的访问,保护主题不让“坏人”接近
远程代理:为一个对象在不同的地址空间提供局部代表,在JAVA中,远程代理可以是另一个虚拟机中的对象
保护代理:用于对象应该有不同访问权限的情况。
智能引用的次数:取代了简单的指针,它访问对象时执行一些附加操作,比如计算对象被引用的次数。
写时复制代理:通常用于复制一个庞大对象的情况。写时复制代理延迟了复制的过程,当对象被真正修改时,才对它进行复制操作。写时复制代理是虚拟代理的一种变体,DLL(操作系统中的动态链接库)是其典型运用场景

你可能感兴趣的:(第6章代理模式)