您的位置:首页 > 其它

设计模式——代理模式详解(教你如何用正确的姿势逃课)

2016-11-22 20:29 369 查看
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的博客   然后请点下面的赞~希望得到你们的支持。
最后,请大家尽量不要逃课哦~

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: