前言
这是本人的设计模式学习笔记,把自己学习过程中的一些总结和认识记录下来,与诸君共勉。本日为大家带来代理模式。
基本概念
代理的任务是对本体的访问进行控制,并暴露出与本体完全相同的接口,将所有对它进行的方法调用传递给本体。注意代理既不像装饰者一样添加行为,也不像外观模式一样简化接口,记住:proxy‘s interface = origin’s interface。下面我们给出代理模式的结构示意图。
具体类别
远程代理
远程代理用于访问位于另一个环境中的对象,另一种环境的意义有着广泛的语义,例如在Java中意味着另一个JVM中得对象。实现远程代理需要有客户端助手也就是代理对象来接受客户的请求并传达给服务端,也要有服务端助手来解析请求。远程对象一般长期存在,下面也给出示意图。远程代理也可以被用来访问其他语言中的对象。
最后,介绍一些术语:
- stub:客户端助手
- skeleton:服务端助手
虚拟代理
虚拟代理用于控制对那种创建开销很大的本体的访问,将本体的实例化推迟到有方法被调用的时候,有时还会提供关于实例化状态的反馈。下面给出一张示意图:
js
proxy.prototype._init=function(){ if(this.entity===null){ this.entity= new Entity(this.data); } };
保护代理
保护代理通常根据客户身份控制对特定方法的访问。
其他代理
这里我们列出一些在上面没有提到的代理:
- 防火墙代理
- 智能引用代理:主题被引用时,进行额外的计算
- 缓存代理:为开销大的运算结果提供暂时存储,允许多用户共享结果以减少实际计算
- 同步代理:多线程情况下位主题提供安全的访问
- 复杂隐藏代理:用来隐藏一个类的复杂集合的复杂度,并进行访问控制
- 写入时复制代理:延迟对象的复制直到客户真的需要为止
代理 vs 装饰者
- 装饰者会扩充或修改原对象的功能,代理仅仅控制它的访问,最多添加一些控制代码而不会修改调用方法
- 装饰者模式中被包装对象的实例化过程完全独立,代理模式中本体的实例化是代理实例化的一部分
- 代理不能像装饰者那样互相包装,一次使用一个
应用
先举一个例子,ajax机制中,你想拉取远程资源并进行一些操作,你可以在本地创建代理对象来暴露这些操作接口,然后就像操作本地对象一样完成操作,实际的xhr对象和远程请求等都可以隐藏在代理内部,具体可以参考jQuery的ajax服务。当然往往这种例子不会那么纯粹,它们或多或少带有装饰器、外观模式的种种特点,但它们确实实现了代理的目的,不是吗?
再比如搜索框,它们要查询服务器并拉取数据,但你并非每次访问该页面都使用搜索框啊,使用虚拟代理就可以在用户真的搜索时再请求,当然你会说直接用事件不就得了。但是异步加载的次数和类别够复杂,你可能会希望仅仅对view暴露接口。虚拟代理总会派上用场。
实现
以虚拟代理为例
js
var Proxy=function(){ this.args=arguments; this.initialized=false; this.subject={};//构建的本体 this.class={...};//本体的构造函数 ... var self=this; for(var key in this.class.prototype){ if(typeof this.class.prototype[key] !== "function") continue; (function(methodName){ self[methodName]=function(){ if(!self.initialized) return; return self.subject[methodName].apply(self.subject,arguments); }; })(key); } }; Proxy.prototype._init=function(){...}; Proxy.prototype._checkInitialization=function(){...};//阻止在初始化之前方法被调用 Proxy.prototype._isInitialized=function(){...};
利弊
- 远程代理减少了为访问远程资源而编写的代码数量,并提供单一接口,提高模块性
- 虚拟代理减少实例化开销
- 掩盖了实例化的逻辑,增加了项目的复杂性,增加类的数目
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。