代理模式

613 阅读4分钟

代理模式

定义

为其他对象提供一种代理以控制对这个对象的访问。

优势

隐藏被代理类的实现细节

解耦代理类与被代理类,可以在无法修改被代理类的情况下为被代理类添加额外的行为。

缺点

和其他的包装者一样,代理会造成设计中类的数量增加。

应用场合

  1. 远程代理,为一个对象在不同的地址空间提供局部代表。这样可以隐藏一个对象存在于不同地址空间的事实。
  2. 虚拟代理,根据需要创建开销很大的对象。通过它来存放实例化需要很长时间的真实对象。
  3. 安全代理,用来控制真实对象访问时的权限。
  4. 智能指引,当调用真实的对象时,代理处理另外一些事。

Example

远程代理

目的:管理客户和远程对象之间的交互

  • Client — Server

假如这里有一个天气查询的 App。用户在手机上输入想要查询的城市,点击开始查询。客户端向服务器发送一个网络请求。服务器根据请求参数找到对应结果,通过网络返回。客户端展示查询结果。

在这么一个流程中。我们把客户端,服务器看作一个提供天气查询服务的整体。客户端和服务器分别作为这个整体其中的一个模块。如下图,那么图中的 Repo 作为 WeatherService 的替身,可以认为是一种远程代理。

  • 跨进程调用

在操作系统中,两个进程是被隔离开的,A 进程无法访问 B 进程的内存空间。

在 Android 系统中,有一些操作是需要跨进程通信的,比如启动一个新的 Activity 需要和 ActivityManagerService 进行一些交互。此时,Android 系统通过 Binder 机制,使 Client 端持有 ActivityManagerService 的代理对象,通过这个代理对象与真实的 ActivityManagerService 进程进行通信。如图

虚拟代理

目的:控制访问实例化开销大的对象

虚拟代理作为创建开销大的对象的代表,经常直到我们真正需要一个对象的时候才创建它。

比较典型的用法是 Kotlin 中的 Lazy。

Android 中的 SharedPreference 在首次加载某个 SharedPreference 文件中的某个值的时候,需要先将对应文件中的所有 KeyValue 全部加载。为了降低这次文件加载对于 App 流畅度的影响,我们往往会选择使用 Lazy 将 SharedPreference 对象的初始化延迟到真正使用的时候。

安全代理

目的:控制调用者对对象方法的访问

通过 Java 提供的动态代理技术,在无法修改被代理类的情况下为被代理类添加额外的行为。

比如,我们可以使用代理模式封装一个不支持多进程特性的第三方 Lib。在代理类中限制这个第三方 Lib 的 API 只允许在 UI 进程中调用。

智能指引

目的:调用真实对象时,代理处理另外一些事。

比如 C++ 的智能指针,可以完成引用计数的功能,在对象使用完毕后自动释放内存,防止内存泄漏。

比如 Android Native 层有模仿 Java 的强引用和弱引用的 Strong PointerWeak Pointer,同样是给对象增加引用计数的功能,达到使用完毕后自动释放内存的效果。

比如 Java 中 Collections 类提供的 synchronizedCollection 等方法,可以为普通的 Collection 对象添加线程安全的特性。

其他变体

防火墙代理:控制网络资源的访问

缓存代理:为开销大的运算结果提供暂时存储,也允许多个客户共享结果,以减小计算或网络延迟

写入时复制代理: CopyOnWriteArrayList

和适配器模式的区别

适配器模式是将一个类的接口转换成客户希望的另外一个接口。使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。

和装饰器模式的区别

装饰器模式主要是为对象增加行为。

代理模式可以作为被代理对象的替身,可以避免不想要的访问,隐藏对象在远程运行的事实,在被代理对象未创建完成时对调用作出响应,等等。

装饰器模式只起到装饰点缀的作用,不会实例化被代理对象。

装饰器模式可以很容易的连续将对象包装多次,而用代理模式的意图往往并不是将对象连续包装多次。

和外观模式的区别

外观模式的目的是提供一个简单的接口,让子系统更加容易使用。

Refs

Head First 设计模式

大话设计模式


@Eric