【23种设计模式从零学4-代理模式】

来源:互联网 发布:战舰建模软件 编辑:程序博客网 时间:2024/06/05 23:47

一、代理模式:

      概念:

代理模式(Proxy)是这样一种设计模式:提供了对目标对象另外的访问方式;即通过代理对象访问目标对象.这样做的好处是:可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能。

思想:

这里使用到一个编程思想:不要轻易的去修改别人已经编写好的核心代码,如若想对该核心方法进行不同程度的修饰,可以通过代理的机制区扩展该方法。

举个例子:
      一个生产肥皂的厂家。他要卖肥皂,但是老板人比较实在,图个物美价廉,并不会做些花里胡哨的包装,但生意也并不怎么火爆;突然有个一天,一个生意人看到了其中的商机,想:诶?如果我要是做些包装给他卖个高价一定能卖个好价钱!,于是他就在厂家进货,做了精美的包装纸打上了广告:“天!下!第!一!皂!”这下这肥皂卖的一天比一天火爆。养活了厂家,也发达了生意人,从此厂家也不跟客户打交道了,他只是老老实实的生产肥皂,打交道的活交给了生意人这个代理。
     上面的小例子中:卖肥皂的厂家是目标对象,生意人是代理。代理帮着目标对象完成想完成的事情。

业务接口:

/** * Created by Sunrui * Create Date: 2017/5/16 * Description: 定义一个业务接口 */public interface Soap {    /**     * 卖肥皂接口     */    public void sellSoap();    /**     * 卖沐浴露     */    public void sellBodyWash();}
    委托类:
/** * Created by Sunrui * Create Date: 2017/5/16 * Description: 肥皂厂 * 接口实现类(委托类):实现具体的业务功能 */public class SoapFactory implements Soap{    @Override    public void sellSoap() {        System.out.println("卖肥皂");    }    @Override    public void sellBodyWash() {        System.out.println("卖沐浴露");    }}
    代理类:

/** * Created by Sunrui * Create Date: 2017/5/16 * Description: 生意人 * 代理类(增强SoapFactory的功能) */public class SoapTrader implements Soap{    private SoapFactory soapFactory;    /**     * 重写默认构造函数     * @param soapFactory :真正要执行业务的对象     */    public SoapTrader(SoapFactory soapFactory){        this.soapFactory = soapFactory;    }    @Override    public void sellSoap() {        System.out.println("天!下!第!一!");        // 调用委托类的方法,这是具体的业务方法        soapFactory.sellSoap();        System.out.println("卖出了更多的产品");    }    @Override    public void sellBodyWash() {        System.out.println("天!下!第!一!");        // 调用委托类的方法,这是具体的业务方法        soapFactory.sellSoap();        System.out.println("卖出了更多的产品");    }}
     静态代理测试类:

/** * Created by Sunrui * Create Date: 2017/5/16 * Description: 静态代理测试类 */public class StaticProxyTest {    public static void main(String[] args) {        SoapFactory soapFactory = new SoapFactory();        //在这里传入要调用的业务对象        SoapTrader soapTrader = new SoapTrader(soapFactory);        //开始调用业务对象的增强方法        soapTrader.sellSoap();        soapTrader.sellBodyWash();    }}
 结果:
     天!下!第!一!
     卖肥皂
     卖出了更多的产品
     天!下!第!一!
     卖沐浴露
     卖出了更多的产品

好了,我们的代理就实现了,观察发现代理模式有这样几个特点:

1、代理类和委托类实现同一个业务接口:这样做的好处是制定委托类与代理类的业务规范,即便是委托类里面的实现细节更改了,代理类却不用实现修改代码,这样我们的代理类就可以完全与业务实现细节脱离干系,只关注类的增强,至于你委托类是怎么实现的具体,代理类并不关心。
     2、代理类重写构造方法,得到委托类的实例,这样业务的实现是代理类通过委托类本身来实现的,代理类只需要在做业务外的一些增强就可以。


     产生的问题:
这样一来程序开发中必然会产生过多冗余代码,所有的代理操作除了调用的方法不一样之外,其他的操作都一样。若代理类在程序运行前就已经存在,那么这种代理方式被成为 静态代理。

动态代理

动态代理类的源码是在程序运行期间由JVM根据反射等机制动态的生成,所以不存在代理类的字节码文件。代理类和委托类的关系是在程序运行时确定。 (现在看这句话有点懵逼的同学不要担心,由于本篇主要是讲解设计模式,所以动态代理的机制和反射的知识,之后我会补上一篇,相信你就会理解的很透彻了)。

业务接口:

/** * Created by Sunrui * Create Date: 2017/5/16 * Description: 定义一个业务接口 */public interface DynamicWash {    /**     * 卖肥皂     */    public void sellSoap();    /**     * 卖沐浴露     */    public void sellBodyWash();}

中介类:

在使用动态代理时,需要定义一个位于代理类与委托类之间的中介类,这个中介类被要求必须实现InvocationHandler接口:

public interface InvocationHandler { Object invoke(Object proxy, Method method, Object[] args); }
      从InvocationHandler这个名称我们就可以知道,实现了这个接口的中介类用做“调用处理器”。当我们调用代理类对象的方法时,这个“调用”会转送到invoke方法中,代理类对象作为proxy参数传入,参数method标识了我们具体调用的是代理类的哪个方法,args为这个方法的参数。这样一来,我们对代理类中的所有方法的调用都会变为对invoke的调用,这样我们可以在invoke方法中添加统一的处理逻辑(也可以根据method参数对不同的代理类方法做不同的处理)。
/** * Created by Sunrui * Create Date: 2017/5/16 * Description: 动态代理中介类 */public class MyInvocationHandler implements InvocationHandler {    private Object target;    public MyInvocationHandler(Object target)    {        this.target = target;    }    @Override    public Object invoke(Object proxy, Method method, Object[] args) throws Exception {        //代理类自己添加的与业务无关的逻辑1        System.out.println("最牛逼的洗护用品");        Object result = method.invoke(target, args);        //代理类自己添加的与业务无关的逻辑2        System.out.println("卖出了更多的洗护用品");        return result;    }}
      委托类:
/** * Created by Sunrui * Create Date: 2017/5/16 * Description: 肥皂厂 * 接口实现类(委托类):实现具体的业务功能 */public class DynamicSoapFactory implements DynamicWash {    @Override    public void sellSoap() {        System.out.println("卖肥皂");    }    @Override    public void sellBodyWash() {        System.out.println("卖沐浴露");    }}
      动态生成代理类:

/** * Created by Sunrui * Create Date: 2017/5/16 * Description: 动态代理测试类 */public class DynamicProxyTest {    public static void main(String[] args) {        DynamicSoapFactory dynamicSoapFactory = new DynamicSoapFactory();        InvocationHandler soapFactoryHandler = new MyInvocationHandler(dynamicSoapFactory);        Object proxy = Proxy.newProxyInstance(dynamicSoapFactory.getClass().getClassLoader(),                dynamicSoapFactory.getClass().getInterfaces(), soapFactoryHandler);        ((DynamicWash)proxy).sellSoap();        ((DynamicWash)proxy).sellBodyWash();    }}
       在以上代码中,我们调用Proxy类的newProxyInstance方法来获取一个代理类实例。这个代理类实现了我们指定的接口并且会把方法调用分发到指定的调用处理器。
      中介类持有一个委托类对象引用,在invoke方法中调用了委托类对象的相应方法,把外部对invoke的调用最终都转为对委托类对象的调用。这不就是我们上面介绍的静态代理的一种实现方式吗?实际上,中介类与委托类构成了静态代理关系,中介类是代理类,委托类就是委托类; 与此同时,代理类与中介类也构成一个静态代理关系,中介类是委托类,代理类是代理类。也就是说,动态代理关系由两组静态代理关系组成,这就是动态代理的原理。下面我们来介绍一下如何”指示“以动态生成代理类。
方法声明:

public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException

       方法的三个参数含义分别如下:
         loader:定义了代理类的ClassLoder;
         interfaces:代理类实现的接口列表;
         h:调用处理器,也就是我们上面定义的实现了InvocationHandler接口的类实例;

以上就是动态代理的实现过程。同学们对动态代理是否有了初步的认识呢?那么动态代理到底应该用在哪些地方呢?
     动态代理的应用场景:
     1、动态代理多数使用在一些系统级别与业务无关的重复性工作上,比如业务前后日志的管理,事务控制,权限管理等。他为我们实现冗杂无关业务的逻辑提供了一种方案,包括我们Spring的AOP,还有拦截器的思想,其实就是通过动态代理的机制实现的
      2、让职责更加分明,把无关于业务的逻辑与业务本身解耦,代理只做代理的事情,具体业务的核心实现还是由委托类本身来实现

好了,代理模式就介绍到这了,大家有兴趣可以深入了解一下代理的应用场景,相信结合好场景再来理解代理模式你一定会收获更多。

参考文章:
       http://www.jb51.net/article/86531.htm
       http://rejoy.iteye.com/blog/1627405
       https://www.ibm.com/developerworks/cn/java/j-lo-proxy1/index.html

原创粉丝点击