代理模式介绍
代理模式(Proxy Pattern)也称为委托模式,是结构型设计模式的一种,代理模式在各类开发中运用的相当广泛,不论是j2ee,android还是ios,都能看到它的身影,所以说设计模式无处不在。
代理模式的定义:
为其他对象提供一种代理以控制这个对象的访问。通俗一点:找别人做你想做但是做不了的事情。
代理模式的UML类图,如图:
角色介绍:
(1) 抽象主题(Subject):定义了真实主题(RealSubject)和代理 (Proxy)的公共接口,这样就在任何时候使用真实主题(RealSubject)的地方使用代理(Proxy)。
(2) 代理(Proxy):保存一个引用使得代理可以直接访问真实主题,并提供一个与Subject的接口相同的接口,这样代理就可以代替真实主题。
(3) 真实主题(RealSubject):定义Proxy所代表的真实主题。
(4) 客户类(Client):即使用代理类的类型。
代理模式的简单实现
背景描述
在生活中代理模式也是经常存在,比如2011年老罗针对西门子冰箱门关不严,进行维权。假设找到了律师进行维权,那么这种方式就是代理模式,当然老罗可以自己打官司,也可以自己打官司,有相同的地方,也有不同的地方。
相同的地方在于:
1、 诉讼申请:都需要提交原告的资料,如姓名、年龄、事情缘由、想达到的目的。
2、 诉讼取证:都需要经过法院的取证调查,开庭争辩等过程。
3、 诉讼完成:最后拿到审判结果。
不同地方在于:
1、 节省时间:老罗省事了,让专业的人做专业的事,不需要自己再去了解法院那一套繁琐复杂的流程。
2、 成功性概率:把握更大了。
通过上面的例子,我们注意到代理模式有几个重点。
1、 被代理的角色(老罗)
2、 代理角色(律师)
3、 协议(不管是代理和被代理谁去做,都需要做的事情,抽象出来就是协议)
具体代码实现
定义诉讼协议接口ILawProtocol.java:
/**
*诉讼协议接口:规定诉讼的一般流程
*/
public interface ILawProtocol {
void submit();//提交申请
void burden();//进行举证
void defend();//开始辩护
void finish();//诉讼完成
}
定义具体的诉讼人LaoLuo .java:
/*
* 具体的诉讼人:继承诉讼接口
*/
public class LaoLuo implements ILawProtocol{
@Override
public void submit() {
// TODO Auto-generated method stub
System.out.println("申请:西门子冰箱质量缺陷,特此申请民事仲裁!");
}
@Override
public void burden() {
// TODO Auto-generated method stub
System.out.println("证据:这是购买冰箱的发票,以及冰箱质量问题的视频!");
}
@Override
public void defend() {
// TODO Auto-generated method stub
System.out.println("辩护:我们证据确凿,必须得到应该的赔偿!");
}
@Override
public void finish() {
// TODO Auto-generated method stub
System.out.println("诉讼完成:判决西门子北京分公司在七日之内退还老罗购买冰箱的成本!");
}
}
定义代理律师类Lawyer .java
/*
* 代理律师类:继承诉讼接口
*/
public class Lawyer implements ILawProtocol{
private ILawProtocol mILawSuit;//关联一个诉讼人
public Lawyer(ILawProtocol mILawSuit) {
super();
this.mILawSuit = mILawSuit;
}
@Override
public void submit() {
// TODO Auto-generated method stub
mILawSuit.submit();//提交申请
}
@Override
public void burden() {
// TODO Auto-generated method stub
mILawSuit.burden();//进行举证
}
@Override
public void defend() {
// TODO Auto-generated method stub
mILawSuit.defend();//开始辩护
}
@Override
public void finish() {
// TODO Auto-generated method stub
mILawSuit.finish();//诉讼完成
}
}
开庭审理测试Test.java:
import java.lang.reflect.Proxy;
public class Test {
public static void main(String[] args) {
//构造一个具体要诉讼的人:老罗
ILawProtocol laoluo=new LaoLuo();
/**使用静态代理**/
//构造一个代理律师,并将老罗作为构造参数传进去
ILawProtocol lawyer=new Lawyer(laoluo);
//执行诉讼人的相关行为
lawyer.submit();
lawyer.burden();
lawyer.defend();
lawyer.finish();
}
}
打印结果如下:
诉讼申请:西门子冰箱质量缺陷,特此申请民事仲裁!
诉讼证据:这是购买冰箱的发票,以及冰箱质量问题的视频!
诉讼辩护:我们证据确凿,必须得到应该的赔偿!
诉讼完成:判决西门子北京分公司在七日之内退还老罗购买冰箱的成本!
上文使用的是静态代理,但是静态代理后期扩展和维护比较困难,因为代码写的太死,没有可替换的余地;针对代码写得死能想到什么解决办法?对,就是反射。使用反射可以很到的解决决定加载哪个代理类的问题,避免了每个代理类都要重复写的问题,这就是动态代理。
新建一个动态代理类DynamicProxy .java:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class DynamicProxy implements InvocationHandler{
//继承InvocationHandler接口
private Object obj;//被代理的类引用
public DynamicProxy(Object obj) {//关联被代理的类
this.obj = obj;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
//调用被代理类对象的方法
Object result=method.invoke(obj, args);
return result;
}
}
然后修改客户类如下:
import java.lang.reflect.Proxy;
public class Test {
public static void main(String[] args) {
//构造一个具体要诉讼的人:老罗
ILawProtocol laoluo=new LaoLuo();
/**使用动态代理**/
DynamicProxy dynamicProxy=new DynamicProxy(laoluo);//构造一个动态代理
//获取被代理类老罗的ClassLoader
ClassLoader loader=laoluo.getClass().getClassLoader();
//动态的构造一个代理者律师类
ILawProtocol lawyerT=(ILawProtocol)Proxy.newProxyInstance(
loader,
new Class[]{ILawProtocol.class},
dynamicProxy);
//实现ILawProtocol的四个方法:
lawyerT.submit();
lawyerT.burden();
lawyerT.defend();
lawyerT.finish();
}
}
打印结果同上。
代理模式除了分为静态代理和动态代理两种分法以外,还有其他按使用场景分类:
- 远程代理(Remote Proxy): 为一个位于不同地址空间的的对象提供一个本地的代理。
- 虚拟代理(Virtual Proxy):如果需要创建一个消耗较大的对象,先创建一个消耗较小的对象来表示,真实对象只在需要时才被真实创建。
- 安全代理(Protection Proxy):用来控制对真实对象的访问权限
Android源码中的代理模式分析
Android源码中有不少的代理模式实现,比如源码里的ActivityManagerProxy代理类,其具体代理的是ActivityManagerNative的子类ActivityManagerService,ActivityManagerService 的功能是负责四大组件的启动/切换/调度、进程的管理/调度等; ActivityManagerService 继承自 ActivityManagerNative 类,并实现了 Watchdog.Monitor 和 BatteryStatsImpl.BatteryCallback 接口;而 ActivityManagerNative 继承自 Binder 类,并实现了 IActivityManager 接口。
class ActivityManagerProxy implements IActivityManager{
//代码省略...
}
ActivityManagerProxy 实现了IActivityManager接口,该接口定义了Activity相关的接口方法,其中有一些我们在应用开发中也时常接触到。
public interface IActivityManager extends IInterface{
public int startActivity(){};
public Intent registerReceiver(){};
public void unregisterReceiver(){};
public ComponentName startService(){};
public int stopService(){};
public int bindService(){};
//省略...
}
上面的IActivityManager这个接口类就相当于代理模式中的抽象主题,那么真正的实现主题是什么呢?就是上面我们提到的继承于ActivityManagerNative 的ActivityManagerService类,这几个类之间的关系如下:
通过类图,我们可以清晰的看到ActivityManagerProxy和ActivityManagerNative 都实现了IActivityManager接口,严格的说ActivityManagerProxy就是代理部分,ActivityManagerNative 就是真实部分,但ActivityManagerNative 是一个抽象类,并不过多的处理具体逻辑,大部分具体逻辑由其子类ActivityManagerService承担。
ActivityManagerService是系统级的Service并且运行于独立的进程空间中,可以通过ServiceManager来获取它。而ActivityManagerProxy也是运行在独立的进程空间中,两者并不相同,因此ActivityManagerProxy与ActivityManagerService的通信必定是通过跨进程来进行的,从类图中也可以看到,这里跨进程的实现是基于Android的Binder机制,并且此处的代理模式实质是远程代理模式。
ActivityManagerProxy在实际的逻辑处理中并未过多的被外部类引用,因为在Android中管理和维护Activity相关信息的类是另外一个叫做ActivityManager的类,ActivityManager虽然说是管理者Activity的相关信息,但是实质上其大多数逻辑都是由ActivityManagerProxy承担。
下面的类关系图很好的描述了ActivityManagerProxy与ActivityManager之间的调用关系:
ActivityManagerService里的源码非常的复杂庞大,后续会继续学习并分享给大家。
总结
通过本篇文章需要掌握以下几点:
(1)代理模式的定义及使用场景,并会简单使用;
(2)理解动态代理和静态代理的区别;
(3)理解InvocationHandler和Proxy.newProxyInstance的使用;