4.7-Proxy-代理-结构型模式

目的

为其他对象提供一种代理以控制该对象的访问。 类似网络中的反向代理

示例

需求:

  • 为了改善打开执行速度,文档编辑器应当按需创建开销很大的对象(例如大型图像)
  • 需要隐藏根据需要创建图像这一事实,从而不会使得编辑器的实现复杂化

方法:

  • 使用图像 Proxy 替代真正的图像,Proxy 可以代替一个图像对象,并且在需要(文档编辑器调用图像代理的 Draw 操作)时负责实例化这个图像对象

  • 图像 Proxy 存储图像路径和分辨率,以及对真正的 Image 实例的指引。因此不需要真正实例化这个图像就可以响应格式化程序对图像尺寸的请求

适用性

在需要用比较通用和复杂的对象指针代替简单的指针的时候,使用 Proxy 模式。例如:

  • 远程代理(Remote Proxy)为一个对象在不同的地址空间提供局部代表
  • 虚代理(Virtual Proxy)根据需要创建开销很大的对象。
  • 保护代理 (Protection Proxy)控制对原始对象的访问。保护代理用于对象应该有不同的访问权限的时候。
  • 智能指针(Smart Reference)取代了简单的指针,它在访问对象时执行一些附加操作。它的典型用途包括:
    • 对指向实际对象的引用计数,可用于内存自动回收
    • 当第一次引用一个持久对象时,将它装入内存
    • 读写锁:在访问一个实际对象前,检查是否已经锁定了它,以确保其他对象不能改变它
  • copy-on-write:用代理延迟复杂大对象的拷贝过程,保证只有当这个对象被修改的时候才对它进行拷贝
    • 在实现 Copy-on-write 时必须对实体进行引用计数。
      • 拷贝代理仅会增加引用计数。只有当用户请求一个修改该实体的操作时,代理才会真正地拷贝它。
      • 当引用的数目为零时,这个实体将被删除。

结构

  • Proxy:
    • 基本功能
      • 保存一个引用使得代理可以访问实体。若 RealSubject 和 Subject 的接口相同,Proxy 会引用 Subject。
      • 提供一个与 Subject 的接口相同的接口,这样代理就可以用来替代实体。
      • 控制对实体的存取,并可能负责创建和删除它。
    • 其他功能:
      • Remote Proxy 负责对请求及其参数进行编码,并向不同地址空间中的实体发送已编码的请求。
      • Virtual Proxy 可以缓存实体的附加信息,以便延迟对它的访问。例如,动机一节中提到的 ImageProxy 缓存了图像实体的尺寸。
      • Protection Proxy 检查调用者是否具有实现一个请求所必需的访问权限。
  • Subject(Graphic):定义 RealSubject 和 Proxy 的共用接口,这样就在任何使用 RealSubject 的地方都可以使用 Proxy。
  • RealSubject(Image):定义 Proxy 所代表的实体。

代理根据其种类,在适当的时候向 RealSubject 转发请求

实现

重载运算符

C++支持重载运算符 ->*

  • 重载运算符,使你能够通过 ImagePtr 对象调用 Image 操作
1
2
3
4
5
6
7
Image* ImagePtr: :operator-> () { 
	return LoadImage();
}

Image& ImagePtr::operator*(){ 
	return *LoadImage();
} 

虚代理的例子中,图像应该在一个特定的时刻被装载——在 Draw 操作被调用时——而不是在只要引用这个图像就装载它。 重载访问操作符不能做出这种区分。在这种情况下我们只能人工实现每一个代理操作,向实体转发请求。

Proxy 并不总是需要知道实体的类型

  • 若 Proxy 类能够完全通过一个抽象接口处理它的实体,则无须为每一个 RealSubject 类都生成一个 Proxy 类,Proxy 可以统一处理所有的 RealSubject 类。
  • Proxy 要实例化 RealSubject(例如在虚代理中),那么它们必须知道具体的类

相关模式

  • Adapter(4.1):适配器为它所适配的对象提供了一个不同的接口。
    • 相反,代理提供了与它的实体相同的接口(或实体接口的子集)
  • Decorator(4.4):尽管装饰的实现部分与代理相似,但装饰的目的不一样。
    • 装饰为对象添加一个或多个功能
    • 而代理则控制对对象的访问