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

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的博客   然后请点下面的赞~希望得到你们的支持。

最后,请大家尽量不要逃课哦~




原文地址:https://www.cnblogs.com/qitian1/p/6461466.html