代码段学习设计模式 -- 代理模式

也被称为委托模式,代理模式为其他对象提供一种代理以控制对这个对象的访问。

目录


[TOC]

1、组成及优点

1.1 组成

  • 抽象角色:通过接口或抽象类声明真实角色实现的业务方法。
  • 代理角色:实现抽象角色,是真实角色的代理,通过真实角色的业务逻辑方法来实现抽象方法,并可以附加自己的操作。
  • 真实角色:实现抽象角色,定义真实角色所要实现的业务逻辑,供代理角色调用。

1.2 优点

  • 职责清晰:真实角色就是实现实际的业务逻辑,不用关心其他非本职工作的事务,通过后期代理完成非本职工作。
  • 高扩展性:具体主题角色可以随时发生变化,只要它实现了接口,而不用关心它如何变化,只要接口没变,代理类可以在完全不做修改的情况下使用。
  • 智能化:通过动态代理的方式实现在编码阶段不需要知道代理的对象。
  • 扩展原功能,不侵入原代码。
  • 普通代理模式,在该模式下调用者只知道代理而不用知道真实角色,屏蔽了真实角色的变更对高层模块的影响,在实际项目中,一般都是通过约定来禁止new一个真实的角色。

1.3 缺点

  • 代理类必须实现接口,导致代理类太多;
  • 一旦接口发生变更,代理类也必须同步更新,导致维护成本会比较高;

2、静态代理

1.1 情景模拟

秉承着通过代码段学习设计模式的的宗旨,我们通过生活中一个常见的情景作为一个栗子:小明同学通过代码的方式从国外购买一台 Mac
以下为根据此情况设计的 UML 图:

UML图

1.2 具体代码

1.2.1 定义的接口

/**
 * Time: 2018/6/6  17:45
 * 定义目标对象的接口方法
 * 抽象角色:通过接口或抽象类声明真实角色实现的业务方法。
 */
public interface Subject {
    public  void buybuybuy();
}

1.2.2 目标类(被代理类)

/**
 * Time: 2018/6/6  17:45
 * 小明,真正的想买Mac的对象 = 目标对象 = 被代理的对象 = 委托类
 * 实现抽象目标对象的接口
 * 真实角色:实现抽象角色,定义真实角色所要实现的业务逻辑,供代理角色调* 用。
 */

public class XiaoMing implements Subject {
    private static final String TAG = "_XiaoMing";

    @Override
    public void buybuybuy() {
        Log.e(TAG, "buybuybuy: 小明要买Mac");
    }
}

1.2.3 代理类

/**                                                           
 * Time: 2018/6/7 0007 10:58                                  
 * 真正进行操作的对象 代理对象   
 * 代理角色:实现抽象角色,是真实角色的代理,通过真实角色的业务逻辑方法* 来实现抽象方法,并可以附加自己的操作。                            */                                                           
public class ForginerBuyer implements Subject {               
    private static final String TAG = "_ForginerBuyer";       
    private Subject mSubject;                                 
                                                              
    public ForginerBuyer(Subject mSubject) {                  
        this.mSubject = mSubject;                             
    }                                                         
                                                              
    @Override                                                 
    public void buybuybuy() {                                 
        Log.e(TAG, "buybuybuy: 代购开始购买");                      
        mSubject.buybuybuy();                                 
        Log.e(TAG, "buybuybuy: 代购结束购买");                      
    }                                                         
}                                                             

1.2.4 客户端

public class BuyActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_buy);
        //创建 XiaoMing 实例对象
        Subject mXiaoMing = new XiaoMing();
        //创建 ForginerBuyer 代购对象,并把 XiaoMing 实例对象传入
        Subject mBuyer = new ForginerBuyer(mXiaoMing);
        mBuyer.buybuybuy();
       
    }
}

1.2.5 执行结果

E/_ForginerBuyer: buybuybuy: 代购开始购买
E/_XiaoMing: buybuybuy: 小明要买Mac
E/_ForginerBuyer: buybuybuy: 代购结束购买

1.2.6 小结

    通过以上代码,稍微思考一下其实在平时静态代理十分的常见,就是把真正的对象包装一下成为一个新的类,通过访问这个新的类我们就可以真正的对目标类对象进行操作。但是我们需要让代理类和目标类发生关系 -- 把目标类对象传入代理类中。
    但是难道我们在使用代理模式时必须要首先构建一个抽像对象吗?我个人觉得不是必须的。即使代理类和目标类没有共同的方法,只要把目标类的实例对象传入代理类中,在代理类中调用目标类的相应方法,那么也能够完成代理的最终目的。

3、动态代理

动态代理和静态代理的不同:在程序运行前,代理类不存在,而是在程序运行时通过反射机制生成。
由程序员创建或由特定工具自动生成源代码,再对其编译。在程序运行前,代理类的.class文件就已经存在了。动态代理类:在程序运行时,运用反射机制动态创建而成。

还是通过上面 小明海外代购一台 Mac 的栗子来说明 动态代理的具体实现。

3.1 具体代码

3.1.1 定义接口

/**
 * Time: 2018/6/6  17:45
 * 定义目标对象的接口方法
 * 抽象角色:通过接口或抽象类声明真实角色实现的业务方法。
 */
public interface Subject {
    public  void buybuybuy();
}

3.1.2 目标类

/**
 * Time: 2018/6/6  17:45
 * 小明,真正的想买Mac的对象 = 目标对象 = 被代理的对象 = 委托类
 * 实现抽象目标对象的接口
 * 真实角色:实现抽象角色,定义真实角色所要实现的业务逻辑,供代理角色调* 用。
 */

public class XiaoMing implements Subject {
    private static final String TAG = "_XiaoMing";

    @Override
    public void buybuybuy() {
        Log.e(TAG, "buybuybuy: 小明要买Mac");
    }
}

3.1.3 声明调用处理器类

/** 作用
 * 1.  生成 动态代理对象
 * 2.  指定 代理对象运行目标对象方法时需要完成的 具体任务
 */
public class DynamicProxy implements InvocationHandler {
    private static final String TAG = "DynamicProxy";
    private Object ProxyObject;

    public Object newProxyInstance(Object ProxyObject){
        this.ProxyObject =ProxyObject;
        return Proxy.newProxyInstance(ProxyObject.getClass().getClassLoader(),
                ProxyObject.getClass().getInterfaces(),this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
        Log.e(TAG, "invoke: 代购出门了" );
        Object result = null;
        // 通过Java反射机制调用目标对象方法
        result = method.invoke(ProxyObject, args);
        return result;
    }
}

3.1.4 通过动态代理对象,调用目标对象的方法


public class Main2Activity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main2);
        // 1. 创建调用处理器类对象
        DynamicProxy DynamicProxy = new DynamicProxy();
        // 2. 创建目标对象对象
        XiaoMing mXiaoMing = new XiaoMing();
        // 3. 创建动态代理类 & 对象:通过调用处理器类对 newProxyInstance()
        // 传入上述目标对象对象
        Subject mSubject = (Subject) DynamicProxy.newProxyInstance(mXiaoMing);
        // 4. 通过调用动态代理对象方法从而调用目标对象方法
        // 实际上是调用了invoke(),再通过invoke()里的反射机制调用目标对象的方法
        mSubject.buybuybuy();
    }
}

注:以上代码参考 Carson_Ho的

3.1.5 执行结果

E/_DynamicProxy: invoke: 代购出门了
E/_XiaoMing: buybuybuy: 小明要买Mac

3.2 代码解析

在静态代理模式中,代理类是通过我们我们自己编辑的,根据动态代理模式的特点:在程序运行期生成代理类,那么动态代理类是怎么生成的呢?怎么通过调用动态代理类的实例来调用目标类的方法?下面我们通过具体代码来说明这两个问题

3.2.1 生成代理类

很明显,在具体的调用中我们是通过以下代码生成代理类的:

Subject mSubject = (Subject) DynamicProxy.newProxyInstance(mXiaoMing);

跳转到具体代码(终点关注标注(重要代码)的代码段)

public static Object newProxyInstance(ClassLoader loader,
                                          Class[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException
    {
        Objects.requireNonNull(h);

        final Class[] intfs = interfaces.clone();
        // Android-changed: sm is always null
        // final SecurityManager sm = System.getSecurityManager();
        // if (sm != null) {
        //     checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
        // }

        /*
         * (重要代码)
         * Look up or generate the designated proxy class.
         * 查找或者生成代理类,正是在此处我们通过代码生成了代理类,
         */
        Class cl = getProxyClass0(loader, intfs);

        /*
         * Invoke its constructor with the designated invocation handler.
         */
        try {
                checkNewProxyPermission(Reflection.getCallerClass(), cl);
            // }
            //(重要代码)
            //通过反射获得动态代理的构造器对象
            final Constructor cons = cl.getConstructor(constructorParams);
            final InvocationHandler ih = h;
            if (!Modifier.isPublic(cl.getModifiers())) {
                // Android-changed: Removed AccessController.doPrivileged
                cons.setAccessible(true);
            }
            //(重要代码)
            //通过反射获得代理类的具体对象实例
            return cons.newInstance(new Object[]{h});
        } catch (IllegalAccessException|InstantiationException e) {
            throw new InternalError(e.toString(), e);
        } catch (InvocationTargetException e) {
            Throwable t = e.getCause();
            if (t instanceof RuntimeException) {
                throw (RuntimeException) t;
            } else {
                throw new InternalError(t.toString(), t);
            }
        } catch (NoSuchMethodException e) {
            throw new InternalError(e.toString(), e);
        }
    }

但是具体是怎么生成动态代理类的呢,我们具体参考Class cl = getProxyClass0(loader, intfs);

private static Class getProxyClass0(ClassLoader loader,
                                           Class... interfaces) {
        if (interfaces.length > 65535) {
            throw new IllegalArgumentException("interface limit exceeded");
        }
        return proxyClassCache.get(loader, interfaces);
    }

proxyClassCacheproxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());我们需要关注的是 ProxyClassFactory

public Class apply(ClassLoader loader, Class[] interfaces) {

          ....
                Method[] methodsArray = methods.toArray(new Method[methods.size()]);
                Class[][] exceptionsArray = exceptions.toArray(new Class[exceptions.size()][]);

                /*
                 * Choose a name for the proxy class to generate.
                 */
                long num = nextUniqueNumber.getAndIncrement();
                String proxyName = proxyPkg + proxyClassNamePrefix + num;

                return generateProxy(proxyName, interfaces, loader, methodsArray,
                                     exceptionsArray);
            }
        }

巴拉巴拉一大堆,我不想看这是什么鬼,反正最后来到了了
generateProxy(proxyName, interfaces, loader, methodsArray, exceptionsArray);,那我们来看一个generateProxy(...)

@FastNative
    private static native Class generateProxy(String name, Class[] interfaces,
                                                 ClassLoader loader, Method[] methods,
                                                 Class[][] exceptions);

这是一个 Native方法,具体为生成代理类,我在网上查看一下资料时看到有些博客上可以调用相应的 api 生成具体的可以看到的代理类,但是我在具体的 demo 中不能进行此操作,故此代码跟踪完毕。

3.3 与静态代理相比的优点

当委托类特别多时,即在每个委托类都对应着一套规则(即每一个委托类都对应着一个抽象对象),那么相应的如果使用静态代理模式,那么就需要实现多个相应的代理类,这样代理类个数会比较多。但是如果使用动态代理模式,此时在生成 InvocationHandler相应的实例时传入不同的委托类实例对象,那么则可以实现同事代理多个委托类的功能。

DynamicProxy DynamicProxy = new DynamicProxy();
XiaoMing mXiaoMing = new XiaoMing();
Subject mSubject = (Subject) DynamicProxy.newProxyInstance(mXiaoMing);
mSubject.buybuybuy();
XiaoHong mXiaoHong= new XiaoHong();
Subject2 mSubject2 = (Subject2) DynamicProxy.newProxyInstance(mXiaoHong);
mSubject2 .buybuybuy2();

即在委托类比较多时,建议使用动态代理模式


优秀博客:
代理模式
Java 动态代理
动态代理相对于静态代理的优势
代理模式(Proxy Pattern):动态代理 - 最易懂的设计模式解析

你可能感兴趣的:(代码段学习设计模式 -- 代理模式)