第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(操作系统中的动态链接库)是其典型运用场景
网友评论