装饰模式(Decorator)与动态代理的强强联合

来源:互联网 发布:费舍住宅尺寸数据 编辑:程序博客网 时间:2024/05/29 09:15

        动态代理的最明显的优点就是“在不改变原来已有的代码结构的情况下,对原来的‘真实方法’进行扩展、增强其功能,并且可以达到控制被代理对象的行为的目的。”而且这种优点是由 JVM 在程序运行时动态实现的。 

但是,我发现如果客户端(Client测试类)先要获得动态代理的实例来实现一定操作时,代理实例的这种功能的扩展已经在服务器端(这里我把服务器端理解为 JVM,因为我们就是从 JVM 中获得动态代理类的实例的)预先地实现了。这种扩展、增强对应于上一篇文章中的代码就是动态代理类中的 invoke() 方法中 before 和 after 注释的这两处中(这两处可以不要同时实现,依具体情况而定)。 

也就是说,如果客户端(Client 测试类)一旦获得动态代理类的实例,那么该代理实例中就一定包含了 before 和 after 中的扩展、增强功能。这似乎显得有点“强制性”了,因为如果客户端(Client 测试类)仅仅只需要一个原始的操作,并不希望代理类预先进行扩展,那么这种所谓的动态、灵活就变得不是很受欢迎了。 

我们应该让客户端在使用之前才来确定是否需要增强,但是我们总不能在服务器端预先提供好几个不同的动态代理,同时也不可能设计几个继承了动态代理类(DynamicProxy 类)并覆盖了其 request() 方法的子类吧。为了说明我认为动态代理还不够完善、合理,说了这么多现在终于引出重点了,呼呼… 

装饰模式(Decorator):动态地给一个对象添加一些额外的职责,就增加功能来说,装饰 模式相比生成子类更为灵活。

使用场景:

1、在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责;

2、处理那些可以撤消的职责;

3、当不能采用生成子类的方法进行扩充时。

 

下面是装饰模式的通用类图: 

image

 

这里给出原来上篇文章中的动态代理的类图如下:

wps_clip_image-10331

 为了对达到上面所说的目标,修改类图,其中增加一个ProxyFactory类,它是用于提供动态代理实例的静态工厂类,旨在减少 Client 测试类中的代码。我们将上面两个类图进行整合,如下:其中的 RealSubject类 相当于上面的 ConcreteComponent类,AbstractSubject接口相当于 Component 抽象类,具体类图如下:

 具体代码实现如下:

主题相关类:

    //抽象主题类,这里不能用abstract抽象类,一定要是interface      interface AbstractSubject {          public abstract void request();      }           // 真实主题类,即被代理类      class RealSubject implements AbstractSubject {          public void request() {              System.out.println("RealSubject's request() ...");          }      } 



装饰相关类:


    // 抽象装饰类,一定要以自己的父类、父接口为一个属性      abstract class Decorator implements AbstractSubject {          protected AbstractSubject subject = null;               public Decorator(AbstractSubject subject) {              this.subject = subject;          }      }           // 具体装饰类01      class ConcreteDecorator01 extends Decorator {               public ConcreteDecorator01(AbstractSubject subject) {              super(subject); //调用父类装饰类的构造器          }               /**           * 覆盖继承树上的接口中的request()方法,用于装饰原对象           */         public void request() {              System.out.println("第一层装饰 ... 装饰在原主题之前");              super.subject.request();          }           }           // 具体装饰类02      class ConcreteDecorator02 extends Decorator {               public ConcreteDecorator02(AbstractSubject subject) {              super(subject); //调用父类装饰类的构造器          }               /**           * 覆盖继承树上的接口中的request()方法,用于装饰原对象           */         public void request() {              super.subject.request();              System.out.println("第二层装饰 ... 装饰在原主题之后");          }      } 


动态代理类:

    // 动态代理类,实现InvocationHandler接口      class DynamicProxy implements InvocationHandler {               // 被代理类的实例          Object obj = null;               // 将被代理者的实例传进动态代理类的构造函数中          public DynamicProxy(Object obj) {              this.obj = obj;          }               /**           * 覆盖InvocationHandler接口中的invoke()方法           *            * 更重要的是,动态代理模式可以使得我们在不改变原来已有的代码结构 的情况下,对原来的“真实方法”进行扩展、增强其功能,并且可以达到           * 控制被代理对象的行为,下面的before、after就是我们可以进行特殊 代码切入的扩展点了。           */         public Object invoke(Object proxy, Method method, Object[] args)                  throws Throwable {              /*               * before :doSomething();               */             System.out.println("动态代理为真实主题添加一个方法  ...");              Object result = method.invoke(this.obj, args);                   /*               * after : doSomething();               */             return result;          }      } 



获取动态代理实例的静态工厂类:

    //提供动态代理实例的静态工厂类      class ProxyFactory {          /**           * @param realSubject :指定需要代理的真实主题类的实例           * @return proxy :代理的实例           */         public static AbstractSubject getProxy(AbstractSubject realSubject) {                            // 获得被代理类的类加载器,使得JVM能够加载并找到被代理类的内部结构,以及已实现的interface              ClassLoader loader = realSubject.getClass().getClassLoader();                   // 获得被代理类已实现的所有接口interface,使得动态代理类的实例              Class<?>[] interfaces = realSubject.getClass().getInterfaces();                   // 用被代理类的实例创建动态代理类的实例,用于真正调用处理程序              InvocationHandler handler = new DynamicProxy(realSubject);                            /*               * 使用java.lang.reflect.Proxy类中的静态方法newProxyInstance()获得代理的实例               *                * loader : 被代理类的类加载器 interfaces :被代理类已实现的所有接口,而这些是动态代理类要实现的接口列表 handler               * : 用被代理类的实例创建动态代理类的实例,用于真正调用处理程序               *                * return :返回实现了被代理类所实现的所有接口的Object对象,即动态代理,需要强制转型               */             AbstractSubject proxy = (AbstractSubject) Proxy.newProxyInstance(                      loader, interfaces, handler);                            return proxy;          }      } 



测试类:

    // 测试类      public class Client {          public static void main(String[] args) {                   // 被代理类的实例              AbstractSubject realSubject = new RealSubject();                   // 通过静态工厂获取动态代理的实例              AbstractSubject proxy = ProxyFactory.getProxy(realSubject);                            // 装饰之前打印出该代理实例的名称                    System.out.println("装饰前:" + proxy.getClass().getName());                            // 装饰前使用代理实例进行原始操作              proxy.request();                            System.out.println("\n第一次装饰之后的效果如下:");              proxy = new ConcreteDecorator01(proxy);              System.out.println("\n名称:" + proxy.getClass().getName());              proxy.request();                   System.out.println("\n第二次装饰之后的效果如下:");              proxy = new ConcreteDecorator02(proxy);                   System.out.println("\n名称:" + proxy.getClass().getName());              proxy.request();          }      } 




测试之前记得在上面的类文件中导入相关的包,如下:

import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;



测试结果:

装饰前:DesignPattern.proxy.dynamicProxy.$Proxy0动态代理为真实主题添加一个方法  ...RealSubject's request() ... 第一次装饰之后的效果如下: 名称:DesignPattern.proxy.dynamicProxy.ConcreteDecorator01第一层装饰 ... 装饰在原主题之前动态代理为真实主题添加一个方法  ...RealSubject's request() ... 第二次装饰之后的效果如下: 名称:DesignPattern.proxy.dynamicProxy.ConcreteDecorator02第一层装饰 ... 装饰在原主题之前动态代理为真实主题添加一个方法  ...RealSubject's request() ...第二层装饰 ... 装饰在原主题之后



在上面的测试类中,我们先通过静态工厂类获得动态代理的实例,然后再根据客户的需要,更加灵活地装饰该动态代理的实例,可以在调用真实主题之前扩展,也可以是之后,总之目的就是使其根据客户的意愿的定制特定功能。

此外,测试结果中给出了装饰前后的动态代理实例的名称,我们发现在客户端中一旦装饰了该动态代理的实例之后,就会同时改变其具体“身份”,毕竟被装饰过“外在形象”也就会跟着改变。

跟现实生活中一样,所谓装饰,就跟我们穿衣服一样,穿不同的衣服就会给他人展现出不同的外在美,呵呵…

其实装饰模式是我在学习 Java IO 中帮助最大的类,之前都被 IO 包中繁杂的类给弄得晕乎乎的,一层层地包装,都不知道那样写代码是什么目的,很难理解…现在倒好了,因为 IO 包中的类就是大量地运用了装饰模式,程序员需要怎样的输入输出,自己动手将其包装一下即可。

0 0
原创粉丝点击