第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请求

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>
<body>
<input type="checkbox" name="" id="1">1
<input type="checkbox" name="" id="2">2
<input type="checkbox" name="" id="3">3
<input type="checkbox" name="" id="4">4
<input type="checkbox" name="" id="5">5
<input type="checkbox" name="" id="6">6
<input type="checkbox" name="" id="7">7
<input type="checkbox" name="" id="8">8
<input type="checkbox" name="" id="9">9

<script>
    var synchronousFile = function(id){
        console.log('开始同步文件,id为:'+'id');
    }
    var checkbox = document.getElementsByTagName('input');
    for(var i = 0,c; c = checkbox[i++];){
        c.onclick = function(){
            if (this.checked === true) {
                synchronousFile(this.id);
            }
        }
    }
</script>
</body>
</html>

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

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<l;i++){
        a = a*arguments[i];
    }
    return a;
}

mult(2,3);//6
mult(2,3,4);//24

//加入缓存代理函数
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);
    }
})();

var a = proxyMult(1,2,3,4); //24
var b = proxyMult(1,2,3,4); //24
console.log(a,b)
开始计算
24 24

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

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

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

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

var mult = function(){
    var a = 1;
    for(var i = 0,l = arguments.length;i<l;i++){
        a = a*arguments[i];
    }
    return a;
}

var plus = function(){
    var a =0;
    for(var i = 0,l=arguments.length;i<l;i++){
        a = a + arguments[i]
    }
    return a;
}

var createProxyFactory = function(fn){
    var cache = {};
    return function(){
        var args =Array.prototype.join.call(arguments,',');
        if(args in cache){
            return cache[args];
        }
        return cache[args] = fn.apply(this,arguments);
    }
};

var proxyMult = createProxyFactory(mult);
var proxyPlus = createProxyFactory(plus);

console.log(proxyMult(1,2,3,4));
console.log(proxyMult(1,2,3,4));
console.log(proxyPlus(1,2,3,4));
console.log(proxyPlus(1,2,3,4));

6.10 其他代理模式

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,802评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,109评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,683评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,458评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,452评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,505评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,901评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,550评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,763评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,556评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,629评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,330评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,898评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,897评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,140评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,807评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,339评论 2 342

推荐阅读更多精彩内容

  • 工厂模式类似于现实生活中的工厂可以产生大量相似的商品,去做同样的事情,实现同样的效果;这时候需要使用工厂模式。简单...
    舟渔行舟阅读 7,715评论 2 17
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,579评论 18 139
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,373评论 25 707
  • 大型经典3D二战坦克塔防手游强势来袭。动态光影的逼真画面,个性化的独特玩法,注重战术的操作体验,爆屏打击的畅快感受...
    SGZKJYXGS阅读 595评论 0 0
  • 因为布局文件的命名中出现两个下划线导致的。比如@id/+id a__b_c
    聂顺阅读 389评论 0 0