标准 专业
多元 极客

设计模式实验室(22)——代理模式

什么是代理模式?

代理模式是一种结构型设计模式。

代理模式是为了给对象提供一个代理或者占位符,由代理对象全权负责对元对象的访问。

比如说,我们想要购物。在以前,我们可能需要去米厂,面厂去排队购买。但是随着时代的进步,这些日常生活中的东西,我们可以通过超市、淘宝、京东等手段进行购买,这种新颖的购物方式其实就是一种代理模式,而超市、淘宝和天猫等购物渠道其实就相当于代理角色。

再举一个我们生活中的例子,我们平时使用的windows操作系统中的快捷方式,其实就是代理模式的体现。

核心结构

  • Subject。抽象主题角色,它通过接口或抽象类的方式,声明了具体主题角色以及代理角色需要实现的业务方法,这样,可以在任何使用具体主题角色的地方使用代理角色。
  • RealSubject。具体主题角色,它实现了抽象主题角色,并定义了需要实现的业务逻辑,客户端可以通过调用代理角色来间接调用真实角色。
  • Proxy。代理角色,它实现了抽象主题角色的同时,也包含了对具体主题角色的引用。代理角色实现抽象主题的原因,是可以在任何时候都可以对具体主题角色进行代理。代理角色还可以对具体主题角色进行控制,在需要的时候创建代理主题角色,不需要时进行删除,方便对具体主题角色进行管理。

类图展示

代理模式

代码分析

代理模式包括远程代理、虚拟代理、保护代理、缓冲代理和智能引用代理等几种实现方式,主要分为静态代理和动态代理两大类,我们将分别对这两种情况举例说明。

静态代理

首先,定义抽象主题角色:

/**
 * Created by <sunshine> mysunshinedreams@163.com on 2017/1/11 0011.
 * 静态代理模式——抽象主题
 */
public interface Push {

    public void push ();
}

接着,定义具体主题角色:

/**
 * Created by <sunshine> mysunshinedreams@163.com on 2017/1/11 0011.
 * 代理模式——具体主题
 */
public class FullAmountPush implements Push {

    @Override
    public void push() {
        System.out.println("full amount push task");
    }
}
/**
 * Created by <sunshine> mysunshinedreams@163.com on 2017/1/11 0011.
 */
public class AppointPush implements Push{

    @Override
    public void push() {
        System.out.println("appoint push task");
    }
}

针对AppointPush对象,生成一个代理角色:

/**
 * Created by <sunshine> mysunshinedreams@163.com on 2017/1/11 0011.
 * 代理模式——代理
 */
public class AppointPushProxy implements Push {

    private AppointPush appointPush = new AppointPush();

    @Override
    public void push() {
        System.out.println("gain the device info");
        appointPush.push();
        System.out.println("push task has done");
    }
}

通过静态代理的实例,我们发现,只需要持有代理角色的对象,即可完成对具体主题角色的调用。

动态代理

通过静态代理模式的代码实例,我们可以很轻易的分析出静态模式的弊端:每个具体主题角色都需要一个一对一的代理角色。这在实际开发过程中可能会生成比较多的类,从而不利于代码的维护。针对这种情况,我们可以使用动态代理模式。

JDK动态代理

还是同样的配方。

抽象主题角色:

/**
 * Created by <sunshine> mysunshinedreams@163.com on 2017/1/11 0011.
 * 静态代理模式——抽象主题
 */
public interface Push {

    public void push ();
}

具体主题角色:

/**
 * Created by <sunshine> mysunshinedreams@163.com on 2017/1/11 0011.
 * 代理模式——具体主题
 */
public class FullAmountPush implements Push {

    @Override
    public void push() {
        System.out.println("full amount push task");
    }
}
/**
 * Created by <sunshine> mysunshinedreams@163.com on 2017/1/11 0011.
 */
public class AppointPush implements Push{

    @Override
    public void push() {
        System.out.println("appoint push task");
    }
}

代理角色,我们将会使用JDK自带的Proxy来实现动态代理模式:

/**
 * Created by <sunshine> mysunshinedreams@163.com on 2017/1/11 0011.
 * 代理模式——代理
 */
public class JDKPushProxy implements InvocationHandler{

    private Push target;

    public JDKPushProxy(Push target) {
        this.target = target;
    }

    @Override
    public Push invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Push result = (Push) method.invoke(target, args);
        return result;
    }

    public Push getProxy() {
        ClassLoader classLoader = target.getClass().getClassLoader();
        // 需要具体的主题角色实现接口
        Class<?>[] interfaces = target.getClass().getInterfaces();
        return (Push) Proxy.newProxyInstance(classLoader, interfaces, this);
    }
}

CGLIB动态代理

CGLIB动态代理比较灵活,由于Java单一继承的约束,所以JDK动态代理只能通过接口来实现。但是CGLIB动态代理则逃脱了这种桎梏,既可以使用继承类来实现,也可以同过实现接口来实现。笔者将通过继承的方式做一个CGLIB动态代理的实例。

抽象主题角色:

/**
 * Created by <sunshine> mysunshinedreams@163.com on 2017/1/11 0011.
 * 代理模式——抽象主题
 */
public abstract class AbstractPush {

    public abstract void push();
}

具体主题角色:

/**
 * Created by <sunshine> mysunshinedreams@163.com on 2017/1/11 0011.
 * 代理模式——具体主题
 */
public class WebspherePush extends AbstractPush {

    @Override
    public void push() {
        System.out.println("websphere push task");
    }
}

通过CGLIB动态代理实现的代理角色。

/**
 * Created by <sunshine> mysunshinedreams@163.com on 2017/1/11 0011.
 * 代理模式——代理
 */
public class CglibPushProxy implements MethodInterceptor {

    private Enhancer enhancer = new Enhancer();

    public Push intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws hrowable {
        return (Push) methodProxy.invokeSuper(o, objects);
    }

    public Push getProxy(Class<?> clazz) {
        enhancer.setSuperclass(clazz);
        enhancer.setCallback(this);
        return (Push) enhancer.create();
    }
}

虽然在这个实例中,笔者展现的是以继承方式实现的CGLIB动态代理,但是在源码中,我们对两种情况都做了测试,具体请移步源码查看。

模式总结

优点

  • 实现了调用者和被调用者之间的解耦,在一定程度上降低了系统的耦合度。
  • 符合开闭原则。因为只需针对抽象主题角色进行变成扩展即可,一般情况下,添加新的代理角色无需修改原有类,同时也保护了原对象。

缺点

  • 有些代理模式的实现很复杂,可能需要大量的额外操作。
  • 由于调用者和被调用者之间出现了一层代理角色,所以可能会影响程序性能。

适用场景

代理模式在我们的编程中使用度还是很频繁的,比如Spring AOP就使用了代理模式,根据代理模式所要表现的行为不同,代理模式也有不同的应用场景:

  • 远程代理。当调用者需要访问远程对象时,可以使用远程代理。
  • 虚拟代理。通过虚拟代理,可以用一个消耗资源较小的对象,代替一个消耗资源较多的对象。
  • 保护代理。对不同角色提供不同权限时可以使用保护代理。
  • 缓冲代理。如果一个对象需要被频繁的使用,可以使用缓冲代理提供为这个对象提供一个临时的存储空间,这样可以减少不必要的资源消耗。
  • 智能引用代理。智能引用代理可以为一个对象的访问提供一些额外的操作。
赞(1) 投币

评论 抢沙发

慕勋的实验室慕勋的研究院

码字不容易,路过请投币

支付宝扫一扫

微信扫一扫