这篇博客我们来介绍一下代理模式(Proxy Pattern),代理模式也成为委托模式,是一个非常重要的设计模式,不少设计模式也都会有代理模式的影子。代理在我们日常生活中也很常见,比如上网时连接的代理服务器地址,更比如我们平时租房子,将找房子的过程代理给中介等等,都是代理模式在日常生活中的使用例子。
代理模式中的代理对象能够连接任何事物:一个网络连接,一个占用很多内存的大对象,一个文件,或者是一些复制起来代价很高甚至根本不可能复制的一些资源。总之,代理是一个由客户端调用去访问幕后真正服务的包装对象,使用代理可以很容易地转发到真正的对象上,或者在此基础上去提供额外的逻辑。代理模式中也可以提供额外的功能,比如在资源集中访问操作时提供缓存服务,或者在操作真正执行到对象之前进行前提条件的检查。对于客户端来说,使用代理对象,和使用真正的对象其实是类似的,因为他们都实现了同样的接口。
转载请注明出处:http://blog.csdn.net/self_study/article/details/51628486。
PS:对技术感兴趣的同鞋加群544645972一起交流。
java/android 设计模式学习笔记目录
代理模式为另一个对象提供一个代理以控制对这个对象的访问。使用代理模式创建代理,让代理对象控制对某个对象的访问,被代理的对象可以是远程的对象,创建开销很大的对象,或者是需要安全控制的对象,所以代理模式的使用场景为:当无法或者不想直接访问某个对象或访问某个对象存在困难时可以通过一个代理对象来间接访问,为了保证客户端的透明性,委托对象与代理对象需要实现同样的接口。
代理模式可以大致分为静态代理和动态代理。静态代理模式的代码由程序员自己或通过一些自动化工具生成固定的代码再对其进行编译,也就是说我们的代码在运行前代理类的 class 编译文件就已经存在;而动态代理则与静态代理相反,在 Java 或者 Android 中通过反射机制动态地生成代理者的对象,也就是说我们在 code 阶段完全不需要知道代理谁,代理谁我们将会在执行阶段决定,在 Java 中,也提供了相关的动态代理接口 InvocationHandler 类,这个我们在后面源码的时候会用到。
代理模式根据实际使用的场景也可以分为以下几种:
我们来看看代理模式的 uml 类图:
据此我们可以写出代理模式的通用代码:
Subject.class
public abstract class Subject {
public abstract void operation();
}
RealSubject.class
public class RealSubject extends Subject{
@Override
public void operation() {
//the real operation
}
}
ProxySubject.class
public class ProxySubject extends Subject{
private Subject realSubject;
public ProxySubject(Subject realSubject) {
this.realSubject = realSubject;
}
@Override
public void operation() {
if (realSubject != null) {
realSubject.operation();
} else {
//do something else
}
}
}
客户端 Client 代码
ProxySubject subject = new ProxySubject(new RealSubject());
subject.operation();
代理模式的角色:
Android 源码里有不少关于代理模式的实现,最典型的比如源码中的 ActivityManagerProxy 代理类,其具体代理的是 ActivityManagerNative 的子类 ActivityManagerService 类,对 AMS 感兴趣的可以去网上查阅一下相关资料,这里就不详细介绍了。我们老规矩,来看看 uml 图:
我在之前的一篇博客:android 不能在子线程中更新ui的讨论和分析 中分析了 Activity 的创建过程,感兴趣的可以去看看,在博客中分析到 startActivity 方法调用到了 Instrumentation 的 execStartActivity 方法, execStartActivity 方法又会调用 ActivityManagerNative.getDefault().startActivity 方法,最后调用到了 ActivityManagerService 中,但是当时并没有很明白为什么会调用到 AMS 中,所以这里根据上面的 uml 图补充说明一下。其实不光是 Instrumentation 类会调用到 ActivityManagerNative.getDefault() 方法,ActivityManager 中的很多方法都调用到了 ActivityManagerNative.getDefault(),比如 moveTaskToFront 方法:
public void moveTaskToFront(int taskId, int flags, Bundle options) {
try {
ActivityManagerNative.getDefault().moveTaskToFront(taskId, flags, options);
} catch (RemoteException e) {
// System dead, we will be dead too soon!
}
}
和 getRunningServices 方法等等等:
public List<RunningServiceInfo> getRunningServices(int maxNum)
throws SecurityException {
try {
return ActivityManagerNative.getDefault()
.getServices(maxNum, 0);
} catch (RemoteException e) {
// System dead, we will be dead too soon!
return null;
}
}
直接将函数调用转给了 ActivityManagerNative.getDefault() 方法,看看 getDefault() 函数:
public abstract class ActivityManagerNative extends Binder implements IActivityManager {
/** * Cast a Binder object into an activity manager interface, generating * a proxy if needed. */
static public IActivityManager asInterface(IBinder obj) {
if (obj == null) {
return null;
}
IActivityManager in =
(IActivityManager)obj.queryLocalInterface(descriptor);
if (in != null) {
return in;
}
return new ActivityManagerProxy(obj);
}
...
static public IActivityManager getDefault() {
return gDefault.get();
}
...
private static final Singleton<IActivityManager> gDefault = new Singleton<IActivityManager>() {
protected IActivityManager create() {
IBinder b = ServiceManager.getService("activity");
if (false) {
Log.v("ActivityManager", "default service binder = " + b);
}
IActivityManager am = asInterface(b);
if (false) {
Log.v("ActivityManager", "default service = " + am);
}
return am;
}
};
...
}
getDefault 函数通过一个 Singleton 对象对外提供, ServiceManager.getService(“activity”) 返回的是 ActivityManagerService 的 Binder 对象,我们在看看 asInterface 函数,如果 queryLocalInterface 方法返回的是 null ,那么就会去创建一个 ActivityManagerProxy 对象,并且将 AMS 这个 Binder 对象作为参数传递给 ActivityManagerProxy 对象,来简单看看 ActivityManagerProxy 的代码:
class ActivityManagerProxy implements IActivityManager
{
public ActivityManagerProxy(IBinder remote)
{
mRemote = remote;
}
public IBinder asBinder()
{
return mRemote;
}
public int startActivity(IApplicationThread caller, String callingPackage, Intent intent,
String resolvedType, IBinder resultTo, String resultWho, int requestCode,
int startFlags, ProfilerInfo profilerInfo, Bundle options) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IActivityManager.descriptor);
...
mRemote.transact(START_ACTIVITY_TRANSACTION, data, reply, 0);
reply.readException();
...
}
...
}
看了这里的代码之后就一目了然了,代理对象的函数比如 startActivity 方法最终调用到的还是 AMS 中,ActivityManagerProxy 的作用只是作为代理而已。调用到 AMS 的后续步骤可以回到博客: android 不能在子线程中更新ui的讨论和分析 去看看,这里就重复了。
还有几点需要在这里特殊说明一下:
/** * Base class for Binder interfaces. When defining a new interface, * you must derive it from IInterface. */
public interface IInterface {
/** * Retrieve the Binder object associated with this interface. * You must use this instead of a plain cast, so that proxy objects * can return the correct result. */
public IBinder asBinder();
}
有一个 asBinder 的方法,而 ActivityManagerProxy 的 asBinder 方法实现在上面已经贴出代码了,所以说 ActivityManagerProxy 中的 asBinder 函数是用来返回 AMS 这个 Binder 对象的;
public ActivityManagerNative() {
attachInterface(this, descriptor);
}
调用到 attachInterface 方法:
public void attachInterface(IInterface owner, String descriptor) {
mOwner = owner;
mDescriptor = descriptor;
}
而第二点中 gDefault 对象的 create 函数是在应用进程中调用的,这是两个不同的进程,所以我们再来看看 asInterface 函数:
static public IActivityManager asInterface(IBinder obj) {
if (obj == null) {
return null;
}
IActivityManager in =
(IActivityManager)obj.queryLocalInterface(descriptor);
if (in != null) {
return in;
}
return new ActivityManagerProxy(obj);
}
然后是 queryLocalInterface 函数:
public IInterface queryLocalInterface(String descriptor) {
if (mDescriptor.equals(descriptor)) {
return mOwner;
}
return null;
}
所以最终这个函数是返回 null 的,也就是说跨进程调用的时候,会 new 一个 ActivityManagerProxy 对象。
看了上面的通用代码和 Android 源码的 AMS 机制之后,代理模式应该已经很清楚了,但是上面用到的都是静态代理,都是在编译阶段生成的 class 文件,所以这里以一个动态+保护代理模式的 demo 为例:
先看看它的 uml 类图:
这里我们用到了 InvocationHandler 这个类,这是 Java 为我们提供的一个便捷动态代理接口,作用就是用来在运行时动态生成代理对象,在这里就不详细介绍了,感兴趣的可以去查阅相关资料。其次是保护代理,这里就以 ProxyA 和 ProxyB 两个代理类为例,一个只能调用 operationA 方法,另一个只能调用 operationB 方法。首先来看看 Subject 和 RealSubject 类:
Subject.class
public interface Subject {
String operationA();
String operationB();
}
RealSubjct.class
public class RealSubject implements Subject{
@Override
public String operationA() {
return "this is operationA";
}
@Override
public String operationB() {
return "this is operationB";
}
}
定义完了抽象主题类和真实主题类之后,开始构造代理类,首先是代理类的虚基类:
ISubjectProxy.class
public abstract class ISubjectProxy implements InvocationHandler {
protected Subject subject;
public ISubjectProxy(Subject subject) {
this.subject = subject;
}
}
然后是 ProxyA 和 ProxyB 这两个代理子类:
ProxyA.class
public class ProxyA extends ISubjectProxy{
public ProxyA(Subject subject) {
super(subject);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (method.getName().equals("operationB")){
throw new UnsupportedOperationException("ProxyA can't invoke operationB");
}else if (method.getName().equals("operationA")) {
return method.invoke(subject, args);
}
return null;
}
}
ProxyB.class
public class ProxyB extends ISubjectProxy{
public ProxyB(Subject subject) {
super(subject);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (method.getName().equals("operationA")){
throw new UnsupportedOperationException("ProxyB can't invoke operationA");
} else if (method.getName().equals("operationB")) {
return method.invoke(subject, args);
}
return null;
}
}
最后是 Client 的代码:
Subject subject = new RealSubject();
ISubjectProxy proxy = null;
switch (v.getId()) {
case R.id.proxy_a:
proxy = new ProxyA(subject);
break;
case R.id.proxy_b:
proxy = new ProxyB(subject);
break;
}
Subject sub = (Subject) Proxy.newProxyInstance(subject.getClass().getClassLoader(),
subject.getClass().getInterfaces(), proxy);
try {
Log.e("Shawn", sub.operationA());
}catch (UnsupportedOperationException e){
Log.e("Shawn", e.getMessage());
}
try {
Log.e("Shawn", sub.operationB());
}catch (UnsupportedOperationException e){
Log.e("Shawn", e.getMessage());
}
结果:
com.android.proxypattern E/Shawn: this is operationA
com.android.proxypattern E/Shawn: ProxyA can't invoke operationB com.android.proxypattern E/Shawn: ProxyB can't invoke operationA
com.android.proxypattern E/Shawn: this is operationB
代码很简单,第一点是使用 InvocationHandler 实现了动态代理,第二点是根据 method 的名字实现了保护代理。
这里只总结了保护代理,虚拟代理这里简单举个例子: moduleA 为 modleB 的 lib,所以如果在 moduleA 中需要用到 moduleB 实现的对象,就可以使用代理模式,具体步骤是在 moduleA 中定义接口,moduleB 去实现该接口,moduleA 中定义一个代理对象并提供一些默认操作,当 moduleB 的相关模块初始化之后,将该对象设置到 moduleA 的代理对象中以替代原来的默认实现,之后代理对象就能成功调用 moduleB 中定义的行为了,而且也实现了延迟加载。远程代理和其他的代理模式大家去网上查阅一下相关资料,万变不离其宗,道理是一样的。
代理模式应用广泛,超级广泛,很多设计模式都会有代理模式的影子,有些模式单独作为一种设计模式,倒不如说是对代理模式的一种针对性优化,而且代理模式的缺点也很少,总结一下代理模式的优缺点:
这四个都是结构型设计模式,他们有些类似,在实际使用过程中也容易搞混,我们在这就给他们做一个对比:
适配器模式和其他三个设计模式一般不容易搞混,它的作用是将原来不兼容的两个类融合在一起,uml 图也和其他的差别很大。
uml 类图:
装饰者模式结构上类似于代理模式,但是和代理模式的目的是不一样的,装饰者是用来动态地给一个对象添加一些额外的职责,装饰者模式为对象加上行为,而代理则是控制访问。
uml 类图:
桥接模式的目的是为了将抽象部分与实现部分分离,使他们都可以独立地进行变化,所以说他们两个部分是独立的,没有实现自同一个接口,这是桥接模式与代理模式,装饰者模式的区别。
uml 类图:
代理模式为另一个对象提供代表,以便控制客户对对象的访问,管理的方式有很多种,比如远程代理和虚拟代理等,这个在上面有,这里就不说了,而装饰者模式则是为了扩展对象。
uml 类图:
https://github.com/zhaozepeng/Design-Patterns/tree/master/ProxyPattern
http://blog.csdn.net/jason0539/article/details/22974405
https://en.wikipedia.org/wiki/Proxy_pattern
http://blog.csdn.net/l2show/article/details/46992495