0%

代理模式

代理模式

代理模式是一种结构型设计模式, 让你能提供真实服务对象的替代品给客户端使用。 代理接收客户端的请求并进行一些处理 (访问控制和缓存等), 然后再将请求传递给服务对象。

问题

当我们的系统过有一些对象是非常缓慢和影响性能的,它可能持有了大量的系统资源或者运行在远端(服务器、其它应用)。我们需要操作它们但并不是总是需要,系统的多个地方可能都会有操作它们的需要,这会对性能造成很大的影响。

解决方案

代理模式通过新建一个与原服务对象接口相同的代理类,系统中需要使用服务对象的位置都替换成使用代理对象,代理对象再将服务请求发送给真正的服务对象去完成工作。

代理对象本身不占用较多资源,代理类在这一过程中还可以完成一些除转发外的其它任务,比如延迟初始化、记录日志、访问控制以及对操作的缓存。

执行远程服务,很多情况下我们需要的服务对象可能不在当前所在的进程空间,它有可能存在于远程服务器,也可能存在与其它应用、其它进程空间里,代理类可以将操作以网络通信、进程间通信的方式转发到远端,处理所有与通信有关的细节。

延迟初始化,运行就占有大量资源的服务对象可以先初始化代理对象,在真正需要的时候再进行初始化,代理类内部也可进行判断是否还有必要继续持有这一占用大量资源的对象,做到及时释放。

访问控制,当不希望所有类都能使用服务对象时,可以通过再代理类中进行调用方的判断,起到访问控制的作用。

记录日志,当我们需要对服务对象的操作进行记录,服务对象又不支持修改时,可以在代理类中输出相关操作日志。

操作缓存,在执行类似下载大文件的操作时,如果多次调用可能会触发多次下载的操作,在代理类中可以对这类操作进行缓存,在真正执行前进行判断和控制。

优点

  • 你可以在客户端毫无察觉的情况下控制服务对象。

  • 如果客户端对服务对象的生命周期没有特殊要求, 你可以对生命周期进行管理。

  • 即使服务对象还未准备好或不存在, 代理也可以正常工作。

  • 开闭原则。 你可以在不对服务或客户端做出修改的情况下创建新代理。

缺点

  • 代码可能会变得复杂, 因为需要新建许多类。
  • 服务响应可能会延迟。

模式结构图

实例

完整代码地址

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class ProxyImage implements Image {
private Image image;
private String fileName;

public ProxyImage(String fileName) {
this.fileName = fileName;
}

@Override
public void displayImg() {
if (image == null) {
image = new RealImage(fileName);
}
image.displayImg();
}
}

具体应用

Android的Aidl会自动生成代理相关代码,client端使用的实际为代理类;

动态代理

静态代理需要为每一个类都创建对应的代理类,而动态代理则不需要。动态代理可以避免静态代理大量重复代码的问题。比如,我们需要统计每个被代理类中方法的处理时间,使用静态代理需要在每个方法调用前后都加上统计时间的代码,如果使用动态代理则不需要这么多重复的代码。动态代理在运行时根据定义好的规则对每个需要代理的类动态生成代理类,然后在系统中用代理类替换原始类。

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
public class ImageInvokeHandler implements InvocationHandler {
private Object mTarget;

public ImageInvokeHandler(Object target) {
this.mTarget = target;
}

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
before();
Object result = method.invoke(mTarget, args);
after();
return result;
}

private void before() {
System.out.println("on invoke before " + new Date());
}

private void after() {
System.out.println("on invoke after " + new Date());
}

}