代理模式
代理是一个对象,它可以用来控制对本体对象的访问,它与本体对象实现了同样的接口,代理对象会把所有的调用方法传递给本体对象的;代理模式最基本的形式是对访问进行控制,而本体对象则负责执行所分派的那个对象的函数或者类,简单的来讲本地对象注重的去执行页面上的代码,代理则控制本地对象何时被实例化,何时被使用;我们在上面的单体模式中使用过一些代理模式,就是使用代理模式实现单体模式的实例化,其他的事情就交给本体对象去处理;
代理的优点:
代理对象可以代替本体被实例化,并使其可以被远程访问;
它还可以把本体实例化推迟到真正需要的时候;对于实例化比较费时的本体对象,或者因为尺寸比较大以至于不用时不适于保存在内存中的本体,我们可以推迟实例化该对象;
1、我们先来理解代理对象代替本体对象被实例化的列子;
比如现在京东ceo想送给奶茶妹一个礼物,但是呢假如该ceo不好意思送,或者由于工作忙没有时间送,那么这个时候他就想委托他的经纪人去做这件事,于是我们可以使用代理模式来编写如下代码:
// 先申明一个奶茶妹对象
var TeaAndMilkGirl = function(name) {
this.name = name;
};
// 这是京东ceo先生
var Ceo = function(girl) {
this.girl = girl;
// 送结婚礼物 给奶茶妹
this.sendMarriageRing = function(ring) {
console.log("Hi " + this.girl.name + ", ceo送你一个礼物:" + ring);
}
};
// 京东ceo的经纪人是代理,来代替送
var ProxyObj = function(girl){
this.girl = girl;
// 经纪人代理送礼物给奶茶妹
this.sendGift = function(gift) {
// 代理模式负责本体对象实例化
(new Ceo(this.girl)).sendMarriageRing(gift);
}
};
// 初始化
var proxy = new ProxyObj(new TeaAndMilkGirl("奶茶妹"));
proxy.sendGift("结婚戒"); // Hi 奶茶妹, ceo送你一个礼物:结婚戒
代码如上的基本结构,TeaAndMilkGirl是一个被送的对象(这里是奶茶妹);Ceo是送礼物的对象,他保存了奶茶妹这个属性,及有一个自己的特权方法sendMarriageRing就是送礼物给奶茶妹这么一个方法;然后呢他是想通过他的经纪人去把这件事完成,于是需要创建一个经济人的代理模式,名字叫ProxyObj;他的主要做的事情是,把ceo交给他的礼物送给ceo的情人,因此该对象同样需要保存ceo情人的对象作为自己的属性,同时也需要一个特权方法sendGift,该方法是送礼物,因此在该方法内可以实例化本体对象,这里的本体对象是ceo送花这件事情,因此需要实例化该本体对象后及调用本体对象的方法(sendMarriageRing).
最后我们初始化是需要代理对象ProxyObj;调用ProxyObj对象的送花这个方法(sendGift)即可;
对于我们提到的优点,第二点的话,我们下面可以来理解下虚拟代理,虚拟代理用于控制对那种创建开销很大的本体访问,它会把本体的实例化推迟到有方法被调用的时候;比如说现在有一个对象的实例化很慢的话,不能在网页加载的时候立即完成,我们可以为其创建一个虚拟代理,让他把该对象的实例推迟到需要的时候。
2、理解使用虚拟代理实现图片的预加载
在网页开发中,图片的预加载是一种比较常用的技术,如果直接给img标签节点设置src属性的话,如果图片比较大的话,或者网速相对比较慢的话,那么在图片未加载完之前,图片会有一段时间是空白的场景,这样对于用户体验来讲并不好,那么这个时候我们可以在图片未加载完之前我们可以使用一个loading加载图片来作为一个占位符,来提示用户该图片正在加载,等图片加载完后我们可以对该图片直接进行赋值即可;下面我们先不用代理模式来实现图片的预加载的情况下代码如下:
第一种方案:不使用代理的预加载图片函数如下
// 不使用代理的预加载图片函数如下
var myImage = (function() {
var imgNode = document.createElement("img");
document.body.appendChild(imgNode);
var img = new Image();
img.onload = function() {
imgNode.src = this.src;
};
return {
setSrc: function(src) {
imgNode.src = "img/g.jpg"
img.src = src;
}
}
})();
// 调用方式
myImage.setSrc("http://img1.imgtn.bdimg.com/i=yxt.jpg");
如上代码是不使用代理模式来实现的代码;
第二种方案:使用代理模式来编写预加载图片的代码如下:
var myImage = (function() {
var imgNode = document.createElement("img");
document.body.appendChild(imgNode);
return {
setSrc: function(src) {
imgNode.src = src;
}
}
})();
// 代理模式
var ProxyImage = (function() {
var img = new Image();
img.onload = function() {
myImage.setSrc(this.src);
};
return {
setSrc: function(src) {
myImage.setSrc("img/g.jpg");
img.src = src;
}
}
})();
// 调用方式
ProxyImage.setSrc("http://img1.imgtn.bdimg.com/i=yxt.jpg");
第一种方案是使用一般的编码方式实现图片的预加载技术,首先创建imgNode元素,然后调用myImage.setSrc该方法的时候,先给图片一个预加载图片,当图片加载完的时候,再给img元素赋值,第二种方案是使用代理模式来实现的,myImage函数只负责创建img元素,代理函数ProxyImage负责给图片设置loading图片,当图片真正加载完后的话,调用myImage中的myImage.setSrc方法设置图片的路径;
他们之间的 优缺点如下:
第一种方案一般的方法代码的耦合性太高,一个函数内负责做了几件事情,比如创建img元素,和实现给未加载图片完成之前设置loading加载状态等多项事情,未满足面向对象设计原则中单一职责原则;并且当某个时候不需要代理的时候,需要从myImage函数内把代码删掉,这样代码耦合性太高。
第二种方案使用代理模式,其中myImage函数只负责做一件事,创建img元素加入到页面中,其中的加载loading图片交给代理函数ProxyImage去做,当图片加载成功后,代理函数ProxyImage会通知及执行myImage函数的方法,同时当以后不需要代理对象的话,我们直接可以调用本体对象的方法即可;
从上面代理模式我们可以看到,代理模式和本体对象中有相同的方法setSrc,这样设置的话有如下2个优点:
用户可以放心地请求代理,他们只关心是否能得到想要的结果。假如我门不需要代理对象的话,直接可以换成本体对象调用该方法即可。
在任何使用本体对象的地方都可以替换成使用代理。
设计模式:http://www.cnblogs.com/tugenhua0707/p/5198407.html