> For the complete documentation index, see [llms.txt](https://suki.gitbook.io/notes/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://suki.gitbook.io/notes/articles/design-pattern/design_pattern_proxy.md).

# 代理模式

## 目的

当调用方不方便直接操作某个对象，又或者希望在访问对象之前或之后做某些操作时，可以使用代理模式。

* 使用一个代理对象作为本体对象的替身；
* 调用方访问代理对象，代理对象做完某些操作后，按照调用方的需求去访问本体对象，把结果返回给调用方；

![https://cdn.jsdelivr.net/gh/suukii/Articles/assets/proxy\_design\_pattern.png](https://cdn.jsdelivr.net/gh/suukii/Articles/assets/proxy_design_pattern.png)

## 问题 & 解决办法

举个例子，我们有一个存放着超多数据的对象，如果把这个对象直接在程序中初始化的话，肯定会占据很多内存；而且，程序不一定一开始就需要用到这个对象，也不一定需要这个对象的所有数据；所以，比较经济的方法是，把这个超大对象放在服务端，当程序需要的时候再发起网络请求去访问。

问题是，在程序中可能不止一处要用到这个对象，我们总不想把发起网络请求的逻辑到处复制粘贴吧。而代理模式的思想就是，提供一个代理对象(替身)，把这个代理对象伪装成本体对象，然后把本体对象藏起来。调用方需要数据的时候，就去访问代理对象，其实调用方是分不清自己在访问代理对象还是本体对象的，它也根本不需要区分。

用图解释大概是这个样子：

![https://cdn.jsdelivr.net/gh/suukii/Articles/assets/proxy\_design\_pattern\_1.png](https://cdn.jsdelivr.net/gh/suukii/Articles/assets/proxy_design_pattern_1.png)

## 应用

代理模式大概有以下几种用途：

* 惰性初始化：在真正需要的时候才通过代理对象去初始化本体对象，一般是本体对象消耗很大的时候才需要这样做；
* 控制访问权限：代理对象负责甄别哪些调用方有权访问本体对象，对有权访问的调用方，代理对象把它们的请求转发给本体对象，对于无权访问的，就无视它们的请求；
* 打印日志：想要记录某个对象的访问日志时，可以用一个代理对象来做这件事，这样就不需要对本体对象做修改；
* 缓存请求结果：就像上例一样，代理对象负责发送网络请求并缓存请求结果，下次有同样的请求时可以直接返回缓存的数据；
* 智能引用：可以用一个代理对象来记录某个对象当前有多少个引用，如果没有调用方引用这个本体对象，这个对象的内存就可以被及时释放了；

## 优点

* 可以通过代理对象来控制本体对象而不必修改调用方代码和本体对象；
* 可以很方便地引入多个代理对象，代理对象们可以链式访问(对代理对象来说，访问另一个代理对象和访问本体对象没什么不同)；

> 唯一的修改是，把调用方的访问对象从本体对象改成代理对象，其他不变，因为代理对象是一个“伪本体对象”，在调用方看来，它和本体对象是一样的。
