菜鸟笔记
提升您的技术认知

代理模式与委托模式的异同点

 在《设计模式之禅》(第二版)中,作者说“代理模式也叫做委托模式”,显然是认为代理模式和委托模式是毫无差别的。然而在实际开发中,我们通常可以很明确的知道一个模式究竟是代理模式还是委托模式,说明两者之间还是有一些区别的。

代理模式和委托模式的相同点很容易理解,都是把业务的需要实现的逻辑交给一个目标实现类来完成。

那么,他们的差别在哪里呢?之前看网上的一个解释说:代理模式的代理类和实现类是上下级关系,而委托模式的委托类和被委托类是平级关系。这么说有一定的道理,不过感觉用这样的平级和上下级的关系来描述这两种设计模式的区别,总是感觉有点牵强。

下面以我们最熟悉的类加载机制以及method.invoke()方法执行过程,来分析一下典型的委托模式的实现过程:

protected Class<?> loadClass(String name, boolean resolve)
        throws ClassNotFoundException
    {
        synchronized (getClassLoadingLock(name)) {
            // First, check if the class has already been loaded
            Class c = findLoadedClass(name);
            if (c == null) {
                long t0 = System.nanoTime();
                try {
                    if (parent != null) {
                        c = parent.loadClass(name, false);
                    } else {
                        c = findBootstrapClassOrNull(name);
                    }
                } catch (ClassNotFoundException e) {
                    // ClassNotFoundException thrown if class not found
                    // from the non-null parent class loader
                }

                if (c == null) {
                    // If still not found, then invoke findClass in order
                    // to find the class.
                    long t1 = System.nanoTime();
                    c = findClass(name);

                    // this is the defining class loader; record the stats
                    sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                    sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                    sun.misc.PerfCounter.getFindClasses().increment();
                }
            }
            if (resolve) {
                resolveClass(c);
            }
            return c;
        }
    }

一般情况下,虚拟机加载一个类时,一般经过这三个步骤:

  1. 首先检查该类有没有被自己加载过,如果加载过了,直接返回加载过的Class类;否则,执行2;
  2. 交给父类加载,如果父类加载过了或者加载成功了,返回父类加载过的Class类;否则,执行3;
  3. 自己加载该类;

再看看method.inovke()方法的执行过程:

    public Object invoke(Object obj, Object[] args)
        throws IllegalArgumentException, InvocationTargetException
    {
        if (++numInvocations > ReflectionFactory.inflationThreshold()) {
            MethodAccessorImpl acc = (MethodAccessorImpl)
                new MethodAccessorGenerator().
                    generateMethod(method.getDeclaringClass(),
                                   method.getName(),
                                   method.getParameterTypes(),
                                   method.getReturnType(),
                                   method.getExceptionTypes(),
                                   method.getModifiers());
            parent.setDelegate(acc);
        }

        return invoke0(method, obj, args);
    }

当invoke()方法执行的次数超过一个阈值是时,委托者会将inovke()方法的具体实现,从JNI的方式切换到动态生成一个java类代替invoke()方法。

类加载机制首先让父类加载,在加载失败的情况下自己尝试加载;而invoke()方法的执行过程有一个被委托者切换的这样一个逻辑存在。与这两者相比,我们思考一下代理模式的实现:

  1. 代理模式下,目标对象从头到尾不会有任何的改变;

  2. 代理方法中不会有任何业务相关的逻辑存在,更不会改变真正的逻辑实现。

与此相反,委托模式不仅可以自由切换被委托者,甚至可以自己实现一段逻辑(例如类加载器);