zoukankan      html  css  js  c++  java
  • 设计模式——代理模式详解(教你如何用正确的姿势逃课)

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

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




  • 相关阅读:
    《数据结构》C++代码 线性表
    《数据结构》C++代码 Splay
    《数据结构》C++代码 前言
    蓝桥杯- 算法提高 最大乘积
    HDU-1241 Oil Deposits
    一个简单的网站计数器
    编写一个jsp页面,输出九九乘法表。
    Sum It Up
    历届试题 剪格子
    历届试题 分糖果
  • 原文地址:https://www.cnblogs.com/qitian1/p/6461466.html
Copyright © 2011-2022 走看看