android 源码设计模式之--代理模式(Proxy)
1 模式定义
1>具有与被代理对象相同的接口(此处的接口是指:方法)的类,客户端必须通过【代理类】来间接的与【被代理目标类】进行交互,
2>需要定义接口或是父类,被代理对象与代码对象共同实现相同的接口划继承机同的父类。(博主理解:解耦的万能方案:中间加一层)
2 应用场景
一般是因为客户端不想直接访问实际的对象,或者访问实际的对象存在困难,因此通过一个代理对象来完成间接的访问。
3 组成部分
Subject【抽象对象角色】 : 声明了目标对象和代理对象的共同接口或父类【动态代理只能是接口】。该类可以是一个抽象类也可以是一个接口
RealSubject【委托对象\目标对象\被代理对象】 : 代理对象所要代表的目标对象。
ProxySubject【代理对象】 : 代理对象内部含有委托对象的引用,从而可以在任何时候操作目标对象;代理对象提供一个与目标对象相同的接口(此处接口是指:方法,如Visit方法),以便可以在任何时候替代目标对象。
从UML图中,可以看出代理类[ProxySubject]与真正实现的类[RealSubject]都是继承了抽象的主题类[Subject],这样的好处在于代理类可以与实际的类有相同的方法,可以保证客户端使用的透明性
3.2模式分类
代理模式可以有两种实现的方式:
静态代理,
另一种是各大框架都喜欢的动态代理。如果你的代理类【RealSubject】存在很多,不方便用静态实现,就可以使用动态代理
两者的区别:动态代理和静态代理的区别就是动态代理不用我们去手动编写代理类。动态代理就是JDK提供的机制,可在内存中动态的生成代理类。
4 场景说明
海外代购,如你现在需要购某一个定制包包,而这包只有国外某专卖店里才有卖,可这段时间你抽不出时间去海外,这时你可以找到专业的海外代购人士,如他们帮你代购回来,
5 示例代码:
5.1 静态代理
5.1.1 Subject.java
package proxy; public interface Subject { public void shopping(); }
5.1.2 RealSubject.java
public class RealSubject implements Subject { @Override public void shopping() { // TODO Auto-generated method stub System.out.println("RealSubject::shopping"); } }
5.1.3 ProxySubject.java
public class ProxySubject implements Subject { private RealSubject mRealSubject; public ProxySubject(RealSubject mRealSubject){ this.mRealSubject = mRealSubject; } @Override public void shopping() { // TODO Auto-generated method stub mRealSubject.shopping(); } }
5.1.4 ClientProxy
public class ClientProxy { public static void main(String[] args) { RealSubject mReal = new RealSubject(); ProxySubject mProxy = new ProxySubject(mReal); mProxy.shopping(); } }
5.1.5 输出打印
通过上面的代理代码,我们可以看出代理模式的特点,代理类接受一个Subject接口的对象(可以任何实现该接口的对象),都可以通过代理类进行代理,增加了通用性。但是也有缺点,每一个代理类都必须实现一遍委托类(也就是realsubject)的接口,如果接口增加方法,则代理类也必须跟着修改。其次,代理类每一个接口对象对应一个委托对象,如果委托对象非常多,则静态代理类就非常臃肿,难以胜任。
5.2 动态代理
定义:代理对象不需要实现接口,但是目标对象需要实现接口,否则不能用动态代理,代理对象的生成是利用JDK的内置API,动态的在内存中构建代码对象。
动态代理也叫:JDK代理,接口代理
动态代理实现方式:通过反射来实现的,借助Java自带的java.lang.reflect.Proxy类,并实现其newProxyinstance() 方法,通过固定的规则生成。
其步骤如下:
编写一个委托类的接口,即静态代理的(Subject接口)
实现一个真正的委托类,即静态代理的(RealSubject类)
创建一个动态代理类,实现InvocationHandler接口,并重写该invoke方法
在测试类中,生成动态代理的对象。
第一二步骤,和静态代理一样,不过说了。
第三步,代码如下: //创建一个动态代理类,实现InvocationHandler接口,并重写该invoke方法
public class DynamicProxy implements InvocationHandler { private Object mObject; public DynamicProxy (Object mObject){ this.mObject = mObject; } @Override public Object invoke(Object arg0, Method arg1, Object[] arg2) throws Throwable { // TODO Auto-generated method stub return arg1.invoke(mObject, arg2); } }
第四步,创建动态代理的对象
public class ClientProxy { public static void main(String[] args) { //(1)静态代理 //RealSubject mRealSubject = new RealSubject(); //ProxySubject mProxy = new ProxySubject(mRealSubject); //mProxy.shopping(); //(2)动态代理 Subject mReal = new RealSubject(); DynamicProxy proxy = new DynamicProxy(mReal); ClassLoader loder = mReal.getClass().getClassLoader(); Subject object = (Subject)Proxy.newProxyInstance(loder, new Class[]{Subject.class}, proxy); object.shopping(); } }
创建动态代理的对象,需要借助Proxy.newProxyInstance。该方法的三个参数分别是:
ClassLoader loader表示指定目标对象的类加载器(获取类加载器的方法是固定的)
Class<?>[] arg1表示目标对象实现的接口类型。
InvocationHandler arg2表示当前的InvocationHandler实现实例对象。\
三:Cglib代理
静态代理与动态代理(JDK代理)都要求目标对象是实现一个接口,但是有时候目标对象只是一个单独的
的对象,并没有实现任何的接口,这个时候可使用目标对象子类来实现代理,这就是Cglib代理
Cglib代理也叫作子类代理,它是在内存中构建一个子类对象从而实现对目标对象功能的扩展,
如何选择代理模式:
1 目标对象需要实现接口,用JDK代理。
2 目标对象不需要实现接口,用Cglib代理
Cglib代理的实现需要引入cglib的jar文件:asm.jar; asm-commons.jar;asm-tree.jar; cglib-2.2.jar
6 小结
静态代理与装饰模式差不多 ^-^
Android源码模式分析:
待续
站在巨人的肩膀 参考文献:
https://www.cnblogs.com/qifengshi/p/6566752.html
https://blog.csdn.net/qian520ao/article/details/72824739
https://www.jianshu.com/p/f64060801691
评论