文章

代理模式

代理模式

本文介绍代理模式定义、结构、特点、适用场景、代码实现。

代理模式

1 模式的定义

代理模式(Proxy Pattern) :给某一个对象提供一个代 理,并由代理对象控制对原对象的引用。代理模式的英 文叫做Proxy或Surrogate,它是一种对象结构型模式。

2 模式的结构

2.1 结构图

代理设计模式的结构

2.2 参与者

  1. 服务接口(Service Interface)声明了服务接口。代理必须遵循该接口才能伪装成服务对象。
  2. 服务(Service)类提供了一些实用的业务逻辑。
  3. 代理(Proxy)类包含一个指向服务对象的引用成员变量。代理完成其任务(例如延迟初始化、记录日志、访问控制和缓存等)后会将请求传递给服务对象。通常情况下,代理会对其服务对象的整个生命周期进行管理。

3 模式分析

3.1 优点

  • 代理模式能够协调调用者和被调用者,在一定程度上降低了系 统的耦合度。
  • 远程代理使得客户端可以访问在远程机器上的对象,远程机器 可能具有更好的计算性能与处理速度,可以快速响应并处理客户端请求。
  • 虚拟代理通过使用一个小对象来代表一个大对象,可以减少系 统资源的消耗,对系统进行优化并提高运行速度。
  • 保护代理可以控制对真实对象的使用权限。

3.2 缺点

  • 由于在客户端和真实主题之间增加了代理对象,因此有些类型的代理模式可能会造成请求的处理速度变慢。
  • 实现代理模式需要额外的工作,有些代理模式的实现 非常复杂。

4 适用环境

  • 延迟初始化(虚拟代理)。如果你有一个偶尔使用的重量级服务对象,一直保持该对象运行会消耗系统资源时,可使用代理模式。

你无需在程序启动时就创建该对象,可将对象的初始化延迟到真正有需要的时候。

  • 访问控制(保护代理)。如果你只希望特定客户端使用服务对象,这里的对象可以是操作系统中非常重要的部分,而客户端则是各种已启动的程序(包括恶意程序),此时可使用代理模式。

代理可仅在客户端凭据满足要求时将请求传递给服务对象。

  • 本地执行远程服务(远程代理)。适用于服务对象位于远程服务器上的情形。

在这种情形中,代理通过网络传递客户端请求,负责处理所有与网络相关的复杂细节。

  • 记录日志请求(日志记录代理)。适用于当你需要保存对于服务对象的请求历史记录时。代理可以在向服务传递请求前进行记录。
  • 缓存请求结果(缓存代理)。适用于需要缓存客户请求结果并对缓存生命周期进行管理时,特别是当返回结果的体积非常大时。

代理可对重复请求所需的相同结果进行缓存,还可使用请求参数作为索引缓存的键值。

  • 智能引用。当一个对象被引用时提供一些额外的操作,比如将对象被调用的次数记录下来等。可在没有客户端使用某个重量级对象时立即销毁该对象。

代理会将所有获取了指向服务对象或其结果的客户端记录在案。代理会时不时地遍历各个客户端,检查它们是否仍在运行。如果相应的客户端列表为空,代理就会销毁该服务对象,释放底层系统资源。代理还可以记录客户端是否修改了服务对象。其他客户端还可以复用未修改的对象。

5 实现方式

  • 如果没有现成的服务接口,你就需要创建一个接口来实现代理和服务对象的可交换性。从服务类中抽取接口并非总是可行的,因为你需要对服务的所有客户端进行修改,让它们使用接口。备选计划是将代理作为服务类的子类,这样代理就能继承服务的所有接口了。
  • 创建代理类,其中必须包含一个存储指向服务的引用的成员变量。通常情况下,代理负责创建服务并对其整个生命周期进行管理。在一些特殊情况下,客户端会通过构造函数将服务传递给代理。
  • 根据需求实现代理方法。在大部分情况下,代理在完成一些任务后应将工作委派给服务对象。
  • 可以考虑新建一个构建方法来判断客户端可获取的是代理还是实际服务。 你可以在代理类中创建一个简单的静态方法,也可以创建一个完整的工厂方法。
  • 可以考虑为服务对象实现延迟初始化。

6 代码实现

https://github.com/august295/DesignPatternCode

参考

[1] [https://design-patterns.readthedocs.io/zh_CN/latest/structural_patterns/proxy.html

[2] https://refactoringguru.cn/design-patterns/proxy

本文由作者按照 CC BY 4.0 进行授权