初探代理模式

代码链接

koonchen/design-patterns/proxy

要点

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

当我们打开某个图形编辑器的时候,很多图形对象的创建开销很大,但是打开它需要很迅速,所以应该按需创建。我们如何才能隐藏根据需要创建图像这一事实,从而不会让编辑器的实现复杂化?

问题的解决是使用另外一个对象,即图像 Proxy ,代替那个真正的图像。 Proxy 可以代替一个图像对象,并且在需要时负责实例化图像对象。

代理模式的适用性:

  • 远程代理:为一个对象在不同的地址空间提供局部代表。

  • 虚代理:根据需要创建开销很大的对象。

  • 保护代理:控制对原始对象的访问。保护代理用于对象应该有不同访问权限的时候。

  • 智能指引:取代了简单的指针,它在访问对象时执行了一些附加操作:

  • 对指向实际对象的引用计数,这样当该对象没有引用的时候,可以自动释放它。
  • 当第一次引用一个持久对象时,将它装入内存。
  • 在访问一个实际对象前,检查时候已经锁定了它,以确保其他对象不能改变它。

代理模式的参与者:

  • Proxy — 保存一个引用使得代理可以访问实体。代替实体,同时它控制对实体的读取、创建、删除。
  • Subject — 定义 RealSubject 和 Proxy 的共用接口,这样就在任何使用 RealSubject 的地方都可以使用 Proxy 了。
  • RealSubject — 定义 Proxy 所代表的实体。

现在,代理模式已经显而易见了,结构如下:

cover

Proxy 模式在访问对象时引入了一定程度的间接性,根据代理的类型,附加了很多用途:

  • Remote Proxy 可以隐藏一个对象存在于不同地址空间的事实。
  • Virtual Proxy 可以进行最优化,例如根据要求创建对象。
  • Protection Proxies 和 Smart Reference 都允许在访问一个对象时有一些附加的内务处理。

需要注意的是,还有一种被称为 copy-on-write 的优化方式,该优化根据需要创建的对象有关,用代理延迟拷贝的过程,因为它没有被修改的时候,没有必要因为拷贝而产生开销。只有用户请求修改实体的操作时,代理才会真正拷贝它。

实现

Proxy 模式可以利用以下一些语言特性:

  • 重载 C++ 中的存取运算符。
  • 使用 Smalltalk 中的 doesNotUnderstand , Smalltalk 提供一个 hook 方法可以用来自动转发请求。当用户向接受者发送一个消息, Proxy 可以重定义 doesNotUnderstand 以便它的实体转发这个消息。
  • Proxy 并不总是需要知道实体的类型,若 Proxy 类能够完全通过一个抽象接口处理它的实体,则无需为每一个 RealSubject 都生成一个 Proxy 类, Proxy 类统一处理所有的 RealSubject 类。当然,如果是实例化,那么是需要知道具体类的。

Java 实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
interface Subject {
void request();
}

class RealSubject implements Subject {

@Override
public void request() {
System.out.println("RealSubject instance has been created");
}
}

class Proxy implements Subject {

private final Subject realSubject;

public Proxy(Subject realSubject) {
this.realSubject = realSubject;
}

@Override
public void request() {
realSubject.request();
}
}

public class ProxyClient {
public static void main(String[] args) {
Proxy proxy = new Proxy(new RealSubject());
proxy.request();
}
}

python 实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Subject(object):
def request(self):
print('Subject instance has been created')

class RealSubject(Subject):
def request(self):
print('RealSubject instance has been created')

class Proxy(Subject):
def __init__(self, subject):
self._subject = subject
def request(self):
self._subject.request()

if __name__ == '__main__':
proxy = Proxy(RealSubject())
proxy.request()

实现异曲同工,不再赘述。

相关模式:

  • Adapter :适配器为它所适配的对象提供了一个不同的接口。相反的,代理提供了与它实体相同的接口。
  • Decoratro :它和 Proxy 很像,但是目的不同,Decorator 为对象添加一个或多个功能,而代理则控制对对象的访问。