技术干货首页投稿(暂停使用,暂停投稿)程序员

设计模式 ——— 代理模式

2017-09-10  本文已影响0人  tomas家的小拨浪鼓

意图

为其他对象提供一种代理以控制对这个对象的访问

代理模式通过代理目标对象,把代理对象插入到客户和目标对象之间,从而为客户和目标对象引入一定的间接性,正是这个间接性,给了代理对象很多的活动空间,代理对象可以在调用具体的目标对象前后,附加很多操作,从而实现新的功能或是扩展目标对象的功能,更狠的是,代理对象还可以不去创建和调用目标对象,也就是说,目标对象被完全代理掉了,或是被替换掉了。

功能

代理模式是通过创建一个代理对象,用这个代理对象去代表真实的对象,客户端得到这个代理对象过后,对客户端没有什么影响,就跟得到了真实对象一样来使用。

当客户端操作这个代理对象的时候,实际上功能最终还是会由真实的对象来完成,只不过是通过代理操作的,也就是客户端操作代理,代理操作真正的对象。

结构

代理模式结构图

代理的分类

Java中的代理

Java对代理模式提供了内建的支持,在java.lang.reflect包下面,提供了一个Proxy的类和一个InvocationHandler的接口。

通常把前面自己实现的代理模式,称为Java的静态代理。这种实现方式有一个较大的缺点,就是如果Subject接口发生变化,那么代理类和具体的目标实现都要变化,不是很灵活,而使用Java内建的对代理模式支持的功能来实现则没有这个问题。

通常把使用Java内建的对代理模式支持的功能来实现的代理称为Java的动态代理。动态代理跟静态代理相比,明显的变化是:静态代理实现的时候,在Subject接口上定义很多的方法,代理类里面自然也要实现很多方法;而动态代理实现的时候,虽然Subject接口上定义了很多方法,但是动态代理类始终只有一个invoke方法。这样当Subject接口发生变化的时候,动态代理的接口就不需要跟着变化了。

注意:Java的动态代理目前只能代理接口,基本的实现是依靠Java的反射机制和动态生成class的技术,来动态生成被代理的接口的实现对象。如果要实现类的代理,可以使用cglib、Javassist。

相关模式

示例

Subject:

public abstract class Subject
{
    public abstract void request();
}

RealSubject:

public class RealSubject extends Subject
{
    public void request()
    {
        System.out.println("From real subject.");
    }
}

ProxySubject:

public class ProxySubject extends Subject
{
    private RealSubject realSubject; //代理角色内部引用了真实角色
    
    public void request()
    {
        this.preRequest(); //在真实角色操作之前所附加的操作
        
        if(null == realSubject)
        {
            realSubject = new RealSubject();
        }
        
        realSubject.request(); //真实角色所完成的事情
        
        this.postRequest(); //在真实角色操作之后所附加的操作
    }
    
    private void preRequest()
    {
        System.out.println("pre request");
    }
    
    private void postRequest()
    {
        System.out.println("post request");
    }
}

客户端使用:

public class Client
{
    public static void main(String[] args)
    {
        Subject subject = new ProxySubject();
        
        subject.request();
    }
}

Subject:

public interface Subject
{
    public void request();
}

RealSubject:

public class RealSubject implements Subject
{
    public void request()
    {
        System.out.println("From real subject!");
    }

}

DynamicSubject:


/**
 * 该代理类的内部属性是Object类型,实际使用的时候通过该类的构造方法传递进来一个对象
 * 此外,该类还实现了invoke方法,该方法中的method.invoke其实就是调用被代理对象的将要
 * 执行的方法,方法参数是sub,表示该方法从属于sub,通过动态代理类,我们可以在执行真实对象的方法前后
 * 加入自己的一些额外方法。
 *
 */

public class DynamicSubject implements InvocationHandler
{
    private Object sub;
    
    public DynamicSubject(Object obj)
    {
        this.sub = obj;
    }
    
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable
    {
        System.out.println("before calling: " + method);
        
        method.invoke(sub, args);
        
        System.out.println(args == null);
        
        System.out.println("after calling: " + method);
        
        return null;
    }
}

注意:invoke的第一个参数Object proxy指的是动态代理的那个对象,也就是我们下面通过Proxy.newProxyInstance(...)方法构建的动态代理对象。

客户端使用:

public class Client
{
    public static void main(String[] args)
    {
        RealSubject realSubject = new RealSubject();

        InvocationHandler handler = new DynamicSubject(realSubject);

        Class<?> classType = handler.getClass();

        Subject subject = (Subject) Proxy.newProxyInstance(classType
                .getClassLoader(), realSubject.getClass().getInterfaces(),
                handler);

        subject.request();

        System.out.println(subject.getClass());

    }
    
}

① static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h):
返回代理类的一个实例,返回后的代理类可以当作被代理类使用(可使用被代理类的在Subject接口中声明过的方法)
这句代码生成的实例,既不是RealSubject实例,也不是DynamicSubject实例。生成的是运行期间动态所生成的实例。
② 所谓Dynamic Proxy是这样一种class:
它是在运行时生成的class,在生成它时你必须提供一组interface给它,然后该class就宣称它实现了这些 interface。你当然可以把该class的实例当作这些interface中的任何一个来用。(Class的实例就可以是任何一个接口)当然,这个Dynamic Proxy其实就是一个Proxy,它不会替你作实质性的工作,在生成它的实例时你必须提供一个handler(InvocationHandler h),由它接管实际的工作
③ subject.request();
不管调用生成代理对象(subject只是个代理)的任何一个方法,流程都会立刻转换到了handler里的invoke方法。

所以我们在使用动态代理的时候,一般需要通过Proxy的静态方法来生成一个动态代理类,然后我们就可以使用这个动态代理类来替代真是的类了。
动态代理类可以实现真实类所实现的所有接口类,同时动态代理类的构建还需要我们传入一个InvocationHandler。InvocationHandler对象会真实替我们完成代理的操作,也就是说我们调用代理类的某个方法后,最终都会转到该对象中通过invoke方法来实现。

参考

《Head First 设计模式》
《设计模式:可复用面向对象软件的基础》
《研磨设计模式》
圣思园 Java SE

上一篇 下一篇

猜你喜欢

热点阅读