0. 前言
写在最前面,本人的设计模式类博文,建议先看博文前半部分的理论介绍,再看后半部分的实例分析,最后再返回来复习一遍理论介绍,这时候你就会发现我在重点处标红的用心,对于帮助你理解设计模式有奇效哦~
在大学里博主难免有事耽误了上课,比如睡觉打游戏=。=,但是又不想老师点名时自己被抓到,毕竟平时分会影响最终成绩。这时候就需要找个没课的好基友帮忙去上课点名了,好基友听我说这个课有好多漂亮妹子,便欣然同意了。当老师点名时,如果好基友没有碰到漂亮妹子,便老老实实的帮我点到了,如果碰到了一个漂亮妹子,便集中精力与人家搭讪,不会再帮我点到了。(蓝瘦,香菇=。=)这个场景就可以用静态代理模式来实现。本文原创,转载请注明出处为SEU_Calvin的博客。
1. 静态代理模式介绍
静态代理模式定义:
为其他对象提供一种代理以控制对这个对象的访问。
静态代理模式的使用场景:
当无法或不想直接访问某个对象时,可以通过一个代理对象来间接访问。
静态代理模式包括的角色:
(1)抽象主题类Subjectt:声明被代理类与代理类的共同接口方法。
(2)被代理类RealSubjectt:客户端通过代理类间接的调用被代理类的方法。
(3)代理类ProxySubjectt:客户端通过代理类间接的调用被代理类的方法,代理类中持有一个被代理的引用。
(4)客户类Client:客户端调用代理类。
2. 静态代理模式实例介绍
通过上面给出的角色类,我们可以把文章开始时的例子实现一下,代码也比较简单,这个实例基本上可以把静态代理类的本质体现出来,那就是通过一个代理对象控制对某对象的访问:
//抽象主题类 public interface Subject{ //老师点名时喊到 void attend(boolean flag); } //被代理类,博主自己 =。= public class Calvin implements Subject{ @Override public void attend(boolean flag){ System.out.println("老师点名Calvin——“到!”"); } } //基友代理类 public class JY implements Subjec{ private Calvin mCalvin; public JY(Calvin mCalvin){ this.mCalvin = mCalvin; } @Override public void attend(boolean flag){ if(flag){ mCalvin.attend(flag); }else{ System.out.println("老师点名Calvin——(好基友在调戏妹子没有答到……)"); } } }最后在客户端类中使用:
Calvin mCalvin = new Calvin(); System.out.println("——第一节课——"); JY mJY = new JY(mCalvin); mJY.attend(true); System.out.println("——第二节课——"); //调用代理类的方法 mJY.attend(false);最后结果输出如下:
那么JY类可以帮其他人喊到吗?当然可以,直接在JY类里将维护的Calvin类修改成Subject类,便可以在客户端类中指定设置JY类代理某个对象,当然这个对象得继承自Subject类。
3. 动态代理模式介绍
有静态代理模式,那肯定会有动态代理模式。
在介绍动态代理模式前,我们需要知道我们为什么需要动态代理模式。毋庸置疑是静态代理模式存在弊端,我们发现在静态代理模式中,代理类JY和被代理类Calvin必须实现同样的接口,如果接口有变动,代理和被代理类都得修改,这也是一个需要解决的问题。
这时候我们就需要引入动态代理模式了。
3.1 动态代理的实现
Java提供了动态的代理接口InvocationHandler,实现该接口需要重写invoke()方法。
下面我们对上例中的代码进行修改,使其成为动态代理模式。
我们这时就不需要JY类了,将其替换为我们的动态代理类DynamicAgent,再将客户端中的代码稍作修改即可。
import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; public class DynamicAent implements InvocationHandler{ private Object obj; public DynamicAent(Object obj){ this.obj=obj; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { String methodName = method.getName(); if (methodName.equals("attend")) { //这里从method和args中获取到在客户端中调用的attend()方法和参数信息 if (args[0] instanceof Boolean && ((Boolean) args[0]) == false) { System.out.println("老师点名Calvin——(好基友在调戏妹子没有答到……)"); return null; } } //通过构造函数传入的代理类 Object result=method.invoke(obj, args); return result; } }客户端类修改如下:
Calvin mCalvin = new Calvin(); System.out.println("——第一节课——"); DynamicAgent dynamicAgent = new DynamicAgent(mCalvin); ClassLoader classLoader = mCalvin.getClass().getClassLoader(); Subject agent =(Subject)Proxy.newProxyInstance(classLoader,mCalvin.getClass().getInterfaces(), dynamicAgent); agent.attend(true); System.out.println("——第二节课——"); agent.attend(false);
我们只需要注意Proxy的newProxyInstance方法:
/* *@作用:生成代理类 *@ClassLoader被代理类的类加载器,用来创建代理类 *@Class<?>[] interfaces 被代理类实现的接口,创建的代理类会实现这些接口 *@InvocationHandler这个接口只有一个 invoke 方法 */ public static Object newProxyInstance(ClassLoader loader, Class<?>[]interfaces, InvocationHandler invocationHandler) throwsIllegalArgumentException { //... }最后输出结果和静态代理模式是一样的。
3.2 静态代理和动态代理的本质区别
静态代理模式在代码运行前,代理类的class编译文件就已经存在了,也就是说我们在编码阶段就已经知道要代理谁了。而动态代理模式则相反,它是根据反射来生成代理类的对象,我们在编码阶段根本不需要知道要代理谁,代理谁会在执行阶段决定。
3.3 动态代理模式下的接口变化
显然在静态代理模式下,如果接口发生变化,那么代理人和被代理人类都需要进行改变。但是动态代理模式只需要修改被代理人即可。
比如Calvin为了让基友代上课的效果更好,安排基友如果自己被老师提问到,那么委托他帮忙回答一下问题。
接口和Calvin实现类修改如下:
public interface Subject { void attend(boolean flag); void answer(boolean flag); } public class Calvin implements Subject{ @Override public void attend(boolean flag) { System.out.println("老师点名Calvin——“到!”"); } @Override public void answer(boolean flag) { System.out.println("老师让Calvin回答问题——“巴拉巴拉回答问题”"); } }
这里依然加入基友勾搭妹子的情节,实现也比较简单,直接在动态代理类的invoke()中加入少量代码即可:
if (methodName.equals("answer")) { //这里从method和args中获取到在客户端中调用的answer()方法和参数信息 if (args[0] instanceof Boolean && ((Boolean) args[0]) == false) { System.out.println("老师点名Calvin回答问题——(好基友在调戏妹子没有答到……)"); return null; } }
动态代理模式下在客户端类中将attend方法直接替换为answer输出如下:
4. 代理模式和装饰者模式的区别
装饰者模式(不了解的同学可以看这篇博文所举的例子)和代理模式实现太像了,如果你看了前面的装饰者模式链接文中的例子,就会发现:
(1)装饰者模式是以客户端透明的方式拓展对象的功能,而代理模式是给对象提供代理对象来控制对该对象的引用。
(2)装饰者模式应该为所装饰的对象增强功能,但代理模式重点是为对象施加控制。
至此关于代理模式的介绍就结束了,代理模式应用很广泛,尤其是在Android的Framework层中,所以还是值得好好研究一下的。
请尊重原创,转载请注明出处:SEU_Calvin的博客 然后请点下面的赞~希望得到你们的支持。
最后,请大家尽量不要逃课哦~