【设计模式】代理模式

原创 lingwer111 随笔 JavaScript 218阅读 2017-05-14 00:57:28 举报

前言
代理模式是非常有意义的模式,在生活中可以找到很多代理模式的场景。比如,明星的经纪人作为代理,想请明星来办一场演出,只能联系他的经纪人,经纪人会把商业演出的细节和报酬都谈好。再把合同交给明星签字。
代理模式的关键是,当客户不方便直接访问一个对象或者不满足需求的时候,提供一个替身来控制对对象的访问。客户实际上访的是替身对象。替身对象读请求做出一些处理后,再把请求转交给本体对象。

第一个例子
下面从一个小例子来属性代理模式的结构。
小明遇到了他的女神A。决定向A送一束鲜花来表白。
javascript 代码

女神拒绝了小明的花,因为送花的时间女神A正心情不好,固然以悲剧收场。刚好打听到他和女神A之间有一个共同的朋友B。于是内向的小明决定让B来代替自己完成送花这件事情。朋友B知道女神A什么时候心情好。于是朋友B选择了一个最佳实际把鲜花转送给了女神A。代码如下
html 代码

虽然女神A依旧拒绝了小明的鲜花。因为追MM的最好的方式,是送一辆宝马。不过这个追女神的例子让我们了解了代理模式的结构。下面再来看看代理模式在实际的工作场景中的应用

代理实现图片预加载
在web开发中,图片预加载是一种常见的技术,如果直接给某个img标签节点设置src属性,由于图片过大或者网络不加,图片的位置往往有段时间会是一片空白。常见的做法是先用一张loading图片占位,然后用异步的方式加载图片,等图片加载好了再把它填充到img节点里,这种场景很适合用虚拟代理。
下面我们来实现这个虚拟代理,首先创建一个普通的本体对象,这个对象复杂往页面中创建一个img标签。并且提供一个对外的setSrc接口,外界调用这个接口。就可以给该IMG标签设置src属性了。
javascript 代码

我们把网速调整到5kb/s。然后通过MyImage.src设置src。可以看到在图片被加载好之前,网页中有一段长长的空白时间
现在开始引入代理对象proxyImage。通过这个代理对象,图片被真正加载好之前。页面中将出现一张站位的菊花图,来提示用户图片正在加载,代码如下
html 代码

现在我们通过proxyImage间接范文myImage,控制了客户对MyImage的访问。并且在此过程中加入一些额外的操作,比如真正在图片加载之前,先把img节点设置为加载图。

代理的意义
也许上面预加载图片功能,不使用任何模式,也能写出来,代码如下
html 代码

为了说明代理模式的意义。下面要引入一个面向对象设计的原则, 单一职责原则
单一职责原则指的是,就一个类(通常也包括对象和函数等)而言,应该仅有一个引起它变化的原因。如果一个对象承担了多项职责,就意味着这个对象将变得巨大,引起它变化的原因可能会有多个。面向对象设计鼓励将行为分布到细粒度的对象之中,如果一个对象承担的职责过多,
等于把这些职责耦合到了一起,这种耦合会导致脆弱和低内聚的设计。当变化发生时,设计可能会遭到意外的破坏。
职责被定义为“引起变化的原因”。上段代码中的 MyImage 对象除了负责给 img 节点设置 src外,还要负责预加载图片。我们在处理其中一个职责时,有可能因为其强耦合性影响另外一个职责的实现。
另外,在面向对象的程序设计中,大多数情况下,若违反其他任何原则,同时将违反开放—封闭原则。如果我们只是从网络上获取一些体积很小的图片,或者 5 年后的网速快到根本不再需
要预加载,我们可能希望把预加载图片的这段代码从 MyImage 对象里删掉。这时候就不得不改动MyImage 对象了。
实际上,我们需要的只是给 img 节点设置 src,预加载图片只是一个锦上添花的功能。如果
能把这个操作放在另一个对象里面,自然是一个非常好的方法。于是代理的作用在这里就体现出来了,代理负责预加载图片,预加载的操作完成之后,把请求重新交给本体 MyImage。
纵观整个程序,我们并没有改变或者增加 MyImage 的接口,但是通过代理对象,实际上给系统添加了新的行为。这是符合开放—封闭原则的。给 img 节点设置 src 和图片预加载这两个功能,
被隔离在两个对象里,它们可以各自变化而不影响对方。何况就算有一天我们不再需要预加载,那么只需要改成请求本体而不是请求代理对象即可。

缓存代理
缓存代理可以为一些开销大的运算结果提供暂时的存储,在下次运算是,如果传递进来的参数和之前一样,则可以直接返回前面存储的结果。
代理缓存的例子-乘积计算
javascript 代码

我们常常在项目中遇到分页的需求,同一页的数据理论上只需去后台拉取一次,这些已经拉取到的数据在某个地方被缓存之后,下一次再请求同一页的时候,遍可以直接使用之前的数据
显然这里也引入缓存代理,实现方式根计算乘积的例子差不多,唯一不同的是,请求数据是个异步操作,我们无法直接把计算结果放到代理函数的缓存中,而是要通过回调的方式。思想是这样的。这里不再赘述具体实现。

小结
代理模式包括许多小分类,在 JavaScript 开发中最常用的是虚拟代理和缓存代理。虽然代理模式非常有用,但我们在编写业务代码的时候,往往不需要去预先猜测是否需要使用代理模式。
当真正发现不方便直接访问某个对象的时候,再编写代理也不迟。

评论 ( 0 )
最新评论
暂无评论

赶紧努力消灭 0 回复