“接口隔离” 模式
- 在组织构建过程中,某些接口之间的直接依赖常常会带来很多问题、甚至根本无法实现。采用添加一层间接(稳定)接口,来隔离本来相互紧密关联的接口是一种常见的解决方案。
- 典型模式
- Facade
- Proxy
- Adapter
- Mediator
一、动机
为其他对象提供一种代理以控制对这个对象的访问。
二、场景分析
对一个对象进行访问控制的一个原因是为了只有在我们确实需要这个对象时才对它进行创建和初始化。我们考虑一个可以在文档中嵌入图形对象的文档编辑器,有些图形对象(如大型光栅图像)的创建开销很大,但是打开文档必须很迅速,因此我们在打开文档时应避免一次性创建所有开销很大的对象,因为并非这些对象在文档中都同时可见,所以也没有必要同时创建这些对象。
这一限制条件意味着,对于每一个开销很大的对象,应该根据需要进行创建,当一个图像变为可见时会产生这样的需求**。但是在文档中我们用什么来代替这个图像呢?我们又如何才能隐藏根据需要创建图像这一事实,从而不会使得编辑器的实现复杂化呢?例如,这种优化不应该影响绘制和格式化的代码。**
问题的解决方案是使用另一个对象,即图像Proxy,替代那个真正的图像。Proxy可以代替一个图像对象,并且在需要时负责实例化这个图像对象。
只有当文档编辑器激活图像代理的Draw操作以现实这个图像的时候,图像Proxy才创建真正的图像。Proxy直接将随后的请求转发给这个图像对象。因此在创建这个图像以后,它必须有一个指向这个图像的引用。
我们假设图像存储在一个独立的文件中,这样我们可以把文件名作为对实际对象的引用。**Proxy还存储了图像的尺寸,即它的长和宽,有了图像尺寸,Proxy无需真正实例化这个图像就可以响应格式化程序对图像尺寸的请求。**结构如下图所示:
以下的类图更详细地阐述了这个例子。
文档编辑器通过抽象的Graphic类定义的接口访问嵌入的图像。ImageProxy是那些根据需要创建的图像的类,ImageProxy保存了文件名作为指向磁盘上的图像文件的指针。该文件名被作为一个参数传递给ImageProxy的构造器。
ImageProxy还存储了这个图像的边框以及对真正的Image实例的指引,直到代理实例化真正的图像时,这个指引才有效。Draw操作必须保证在向这个图像转发请求之前,它已经被实例化了。GetExtend操作只有在图像被实例化后才向它传递请求,否则,ImageProxy返回它存储的图像尺寸。
三、模式定义
上述场景分析中涉及到的是一种称为 “虚代理” 的情况,代理模式一共有4种常见情况
3.1 代理模式分类
- **远程代理:**为一个对象在不同的地址空间提供局部代理。
- **虚代理:**根据需要创建开销很大的对象,场景分析中就是这样一种代理的例子。
- **保护代理:**控制对原始对象的访问。保护代理用于对象应该有不同的访问权限的时候。
- **智能指引:**取代了简单的指针,它在访问对象时执行一些附加操作。
类图结构:
3.2 代理模式中的参与者
- Proxy:
- 保存一个引用使得代理可以访问实体。若RealSubject和Subject接口相同,Proxy会引用Subject。
- 提供一个与Subject的接口相同的接口,这样代理接可以用来代替实体
- 控制对实体的存取,并可能负责创建和删除它
- 其他功能依赖于代理的类型:
- Remote Proxy负责对请求及其参数进行编码,并向不同地址空间中的实体发送已编码的请求;
- Vitrual Proxy可以缓存实体的附加信息,以便延迟对它的访问。例如,场景分析中提到的ImageProxy缓存了图像实体的尺寸。
- Protection Proxy检查调用者是否具有实现一个请求所必须的访问权限
- Subject:
- 定义RealSubject和Proxy的功用接口,这样就在任何使用RealSubject的地方都可以使用Proxy。
- RealSubject:
- 定义Proxy所代表的实体
3.3 代理模式起到的效果
根据代理的类型,附加的间接性有多种用途:
- Remote Proxy可以隐藏一个对象存在于不同地址空间的事实
- Vitrual Proxy可以进行最优化,例如根据要求创建对象
- Protection Proxy和Smart Referency都允许在访问一个对象时有一些附加的内务处理。
四、动态代理
**场景引入:**假设我们非常喜欢游戏,但是在游戏中升级太累了,我们就可以花钱找代理来帮我们玩儿,代理要实现我们自己打游戏中的所有步骤,如登陆、打怪、升级,这种大白话映射到程序中的类图如下:
客户端只给代理传递一个名字就可以创建一个代理对象,后续对于操作GamePlayer和操作GamePlayerProxy的流程就一样了
??????????
待补充