代理模式
定义
代理模式(Proxy):为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。
举个栗子:假如我现在想买一辆车,虽然我可以自己去找车源,做质量检测等一系列的车辆过户流程,但是这确实太浪费我得时间和精力了。我只是想买一辆车而已为什么我还要额外做这么多事呢?于是我就通过中介公司来买车,他们来给我找车源,帮我办理车辆过户流程,我只是负责选择自己喜欢的车,然后付钱就可以了。
为什么要使用代理模式
(1)起中介隔离作用:当客户类不想或者不能直接引用一个委托对象(目标对象),而代理类对象可以在客户类和委托对象之间起到一个中介的作用,其特征是代理类和委托类实现相同的接口。
(2)符合开闭原则,增加功能:我们可以通过给代理类增加额外功能来扩展委托类的功能,这样我们只需要修改代理类而不需要修改委托类,符合代码设计的开闭原则。
代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后对返回结果的处理等。代理类本身并不真正实现服务,而是同过调用委托类的相关方法,来提供特定的服务。
真正的业务功能还是由委托类来实现,但是可以在业务功能执行的前后加入一些公共的服务。例如我们想给项目加入缓存、日志这些功能,我们就可以使用代理类来完成,而没必要打开已经封装好的委托类。
代理模式的类型
(1) 远程代理:为位于两个不同地址空间对象的访问提供了一种实现机制。
可以将一些消耗资源较多的对象和操作移至性能更好的计算机上,提高系统的整体运行效率。
(2) 虚拟代理:把一些开销很大的对象,延迟到真正需要它的时候才去创建执行。
通过一个消耗资源较少的对象来代表一个消耗资源较多的对象,可以在一定程度上节省系统的运行开销。
(3) 缓存代理:可以作为一些开销大的运算结果提供暂时的存储,下次运算时,如果传递进来堵塞参数跟之前一致,则可以直接返回前面存储的运算结果。优化系统性能,缩短执行时间。
(4) 保护代理:可以控制对一个对象的访问权限,为不同用户提供不同级别的使用权限(保护代理可以帮助过滤一些不合理的请求)。
代理模式适用场景
代理模式的类型较多,不同类型的代理模式有不同的优缺点,它们应用于不同的场合:
(1) 当客户端对象需要访问远程主机中的对象时可以使用远程代理。
(2)当需要用一个消耗资源较少的对象来代表一个消耗资源较多的对象,从而降低系统开销、缩短运行时间时可以使用虚拟代理,例如一个对象需要很长时间才能完成加载时。
(3)当需要为某一个被频繁访问的操作结果提供一个临时存储空间,以供多个客户端共享访问这些结果时可以使用缓存代理。通过使用缓存代理,系统无须在客户端每一次访问时都重新执行操作,只需直接从临时缓冲区获取操作结果即可。
(4) 当需要控制对一个对象的访问,为不同用户提供不同级别的访问权限时可以使用保护代理。
(5) 当需要为一个对象的访问(引用)提供一些额外的操作时可以使用智能引用代理。
代理模式也有一定的优缺点:
优点:
(1)代理模式能够将代理对象与被调用的对象分离,降低了系统的耦合度。
(2)代理模式在客户端和目标对象之间起到一个中介作用,这样,可以起到保护目标对象的作用。
(3)代理对象也可以对目标对象调用之前进行其他的操作。
缺点:
增加了系统的复杂度
小例子
先来看一个简单的例子。
1、文子想送给喜欢的男孩子一个节日礼物,但是不好意思,于是就找男孩的好朋友,让男孩的朋友把礼物转交到男孩手里。
var Gift=function(name){
this.name=name
this.getName=function(){
return this.name
}
}
var wenzi={
sendGift:function(dl){
var gift=new Gift('手办盲盒')
dl.receiveGift(gift.getName())
}
}
var pengyou={ //朋友代理
receiveGift:function(gift){
nanhai.receiveGift(gift)
}
}
var nanhai={
receiveGift:function(gift){
console.log('谢谢你的礼物'+gift)
}
}
wenzi.sendGift(pengyou)
在这里呢,男孩的朋友就相当于是一个代理。
2、虚拟代理的一个应用在于实现图片预加载。
不使用虚拟代理时加载图片:
var myImage = (function () {
var imgNode = document.createElement('img');
document.body.appendChild(imgNode);
return {
setSrc: function (src) {
imgNode.src = src;
}
}
})();
myImage.setSrc('https://segmentfault.com/img/bVbmvnB?w=573&h=158');
如果图片非常大的话,用户加载图片时会出现很长时间是空白,可以在加载图片完成之前显示一个loading的图片。
使用虚拟代理实现图片预加载:
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() {
// setTimeout(()=>{
myImage.setSrc(img.src)
//},2000)
}
return {
setSrc: function (src) {
img.src = src
myImage.setSrc('https://content.igola.com/static/WEB/images/other/loading-searching.gif') //预加载图片
}
}
})()
proxyImage.setSrc('9.png.gif'); //要显示的图片
//如果图片非常大,先显示预加载图,再显示图片