简介
代理模式也称为委托模式。简单的说就是委托其他的来干我想干的事情。例如代理上网,要别人带个外卖。
定义
为其他对象提供一种代理来控制对对象的访问。
使用场景
当不直接访问对象或者无法访问时可以通过一个代理对象来间接访问,保证客户端的透明性,代理对象和被代理对象需要实现相同的接口。
UML类图
Subject:声明主题和代理共同接口或者方法, 可以是接口或者抽象类
RealSubject:要被代理的对象,它实现或继承了Subject
ProxySubject:代理对象,它包含被代理对象RealSubject作为成员,间接的调用要被代理对象的方法,并且实现了Subject接口。
Client:客户端通过指向Subject来使用实现。
区分
从代码上面来区分可以分为静态代理和动态代理两种方式。从适用范围来区分可以分为:
远程代理:
为某个对象在不同的内存地址空间提供局部代理。系统可以将Server部分的实现隐藏,以便Client不必考虑Server的存在。参考链接虚拟代理:
使用一个代理对象表示一个十分消耗资源的对象并在真正需要时才创建。参考链接保护代理:
使用代理控制对原始对象的访问。该对象的代理常被用于原始对象有不同的访问权限的情况。智能引用:
在访问原始对象时执行一些自己的附加操作,并对指向原始对象的引用计数。
静态代理实现
以小叶找律师上诉为场景。
定义UML中的Subject对象上诉的一些流程方法ILawsuit接口:
public interface ILawsuit {
//提交上诉
void submit();
//进行举证
void burden();
//开始辩护
void defend();
//诉讼完成
void finish();
}
定义UML中的RealSubject对象抽象话上诉的代理人XiaoYe:
public class XiaoYe implements ILawsuit {
@Override
public void submit() {
System.out.println("Xiao ye submit");
}
@Override
public void burden() {
System.out.println("Xiao ye burden");
}
@Override
public void defend() {
System.out.println("Xiao ye defend");
}
@Override
public void finish() {
System.out.println("Xiao ye finish");
}
}
定义UML中的代理对象ProxySubject对象Lawyer类
public class Lawyer implements ILawsuit {
private ILawsuit mCustomer;
public Lawyer(ILawsuit customer) {
if (customer == null) {
throw new IllegalArgumentException("Customer can not be empty");
}
this.mCustomer = customer;
}
@Override
public void submit() {
mCustomer.submit();
}
@Override
public void burden() {
mCustomer.burden();
}
@Override
public void defend() {
mCustomer.defend();
}
@Override
public void finish() {
mCustomer.finish();
}
}
定义客户端类:
ILawsuit xiaoye = new XiaoYe();
ILawsuit lawyer = new Lawyer(xiaoye);
lawyer.submit();
lawyer.burden();
lawyer.defend();
lawyer.finish();
静态代理如上所述,代理者的代码由程序员生成再编译,也可以说在我们的代码运行前代理类的class编译文件已经存在。而动态代理则相反,通过反射机制动态生成代理者对象。我们在编码阶段不知道要代理的是谁,代理谁要在执行阶段决定。使用Java提供的动态代理接口InvocationHandler。
动态代理实现
修改上面的律师类Lawyer为DynamicLawyer类来替代:
public class DynamicLawyer implements InvocationHandler {
private Object mObject;
public Object getInstance(Object object) {
this.mObject = object;
ILawsuit lawyer = (ILawsuit) Proxy.newProxyInstance(mObject.getClass().getClassLoader(), new Class[]{ILawsuit.class}, this);
return lawyer;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return method.invoke(mObject, args);
}
}
修改客户端代码:
private static void dynamicProxyMethod() {
ILawsuit xiaoye = new XiaoYe();
DynamicLawyer dynamicLawyer = new DynamicLawyer();
ILawsuit lawyer = (ILawsuit) dynamicLawyer.getInstance(xiaoye);
lawyer.submit();
lawyer.burden();
lawyer.defend();
lawyer.finish();
}
这样就实现了动态代理。
Android源码使用代理模式
注意我们基于Android7.0的代码来看的并没有贴出代码,重要的是了解其大过程,然后自己阅读源码回顾,有下面的一张图就都在其中了。 ActivityManagerProxy(它是ActivityManagerNative的内部类,开始找不到在哪)我们先有一个大概念,它代理的是ActivityManagerService类(它是Android framework层的一个玩意。)这里涉及到它相关的几个类的类图如下:
这个图包含了它使用代理模式的总体设计。要说明流程如下:
ActivityManagerService是一个系统FrameWork的一个服务它和我们的应用交互不在一个进程,那么通讯方式是通过Binder机制来的。它可以细分是一个远程代理模式。关键描述:
- 客户端获取ActivityManagerService:
- ActivityThread作为客户端调用attach方法里面调用ActivityManagerNative.getDefault()。跟踪源码知道这里返回的是一个ActivityManagerProxy对象。
- 客户端通过获取到的ActivityManagerProxy来调用远程服务ActivityManagerservice的方法以getMemoryInfo为例(这个方法在ActivityManager里面它管理和维护Activity的相关信息),它实际也是调用的代理对象来获取的。
- 我们直接看到ActivityManagerProxy的getMemoryInfo它会去调用mRemote.transact(GET_MEMORY_INFO_TRANSACTION, data, reply, 0) 方法。
- Binder通讯的细节过程客户端通过代理类如何怎么一步一步把消息发送给远程服务,然后远程服务来调用方法返回我们这里不去细究,这里涉及到Binder的通讯机制底层实现,这玩意真难懂。
- 然后到看到ActivityManagerService里面代码接受消息case:GET_MEMORY_INFO_TRANSACTION这里面对获取信息进行处理。它会调用getMemoryInfo方法,这个方法是ActivityManagerService来实现的。
- 通过MemoryInfo为信息载体我们在客户端可以得到内存的信息了
有一个疑惑,客户端的MemoryInfo参数,为啥到了远程的ActivityManagerService里面通过如下代码
ActivityManager.MemoryInfo mi = new ActivityManager.MemoryInfo();
把这个信息给客户端的呢,头真的大。
getMemoryInfo(mi);
总结:
优点:
- 协调代理者何被代理者,降低了系统的耦合度。
- 客户端可以针对抽象主题角色编程,增加和更换代理类不用修改源代码,有更好的灵活和扩展性。
缺点:
- 由于在客户端和真实主题之间增加了代理对象,因此有些类型的代理模式可能会造成请求的处理速度变慢,例如保护代理。
- 实现代理模式需要额外的工作和额外的类,而且有些代理模式的实现过程较为复杂,例如远程代理。