装饰模式(Decorator)与动态代理的强强联合
来源:互联网 发布:费舍住宅尺寸数据 编辑:程序博客网 时间:2024/05/29 09:15
动态代理的最明显的优点就是“在不改变原来已有的代码结构的情况下,对原来的‘真实方法’进行扩展、增强其功能,并且可以达到控制被代理对象的行为的目的。”而且这种优点是由 JVM 在程序运行时动态实现的。
但是,我发现如果客户端(Client测试类)先要获得动态代理的实例来实现一定操作时,代理实例的这种功能的扩展已经在服务器端(这里我把服务器端理解为 JVM,因为我们就是从 JVM 中获得动态代理类的实例的)预先地实现了。这种扩展、增强对应于上一篇文章中的代码就是动态代理类中的 invoke() 方法中 before 和 after 注释的这两处中(这两处可以不要同时实现,依具体情况而定)。
也就是说,如果客户端(Client 测试类)一旦获得动态代理类的实例,那么该代理实例中就一定包含了 before 和 after 中的扩展、增强功能。这似乎显得有点“强制性”了,因为如果客户端(Client 测试类)仅仅只需要一个原始的操作,并不希望代理类预先进行扩展,那么这种所谓的动态、灵活就变得不是很受欢迎了。
我们应该让客户端在使用之前才来确定是否需要增强,但是我们总不能在服务器端预先提供好几个不同的动态代理,同时也不可能设计几个继承了动态代理类(DynamicProxy 类)并覆盖了其 request() 方法的子类吧。为了说明我认为动态代理还不够完善、合理,说了这么多现在终于引出重点了,呼呼…
装饰模式(Decorator):动态地给一个对象添加一些额外的职责,就增加功能来说,装饰 模式相比生成子类更为灵活。
使用场景:
1、在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责;
2、处理那些可以撤消的职责;
3、当不能采用生成子类的方法进行扩充时。
下面是装饰模式的通用类图:
这里给出原来上篇文章中的动态代理的类图如下:
为了对达到上面所说的目标,修改类图,其中增加一个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 包中的类就是大量地运用了装饰模式,程序员需要怎样的输入输出,自己动手将其包装一下即可。
- 装饰模式(Decorator)与动态代理的强强联合
- 装饰模式(Decorator)与代理模式(Proxy)的区别与联系
- 我的设计模式-装饰(Decorator)、代理(Proxy)
- 动态代理与装饰模式
- Java编程开发设计模式之--装饰者模式(Decorator)与代理模式(Proxy)
- 装饰模式(Decorator)和代理模式(Proxy)
- 装饰(Decorator)模式
- 装饰模式(Decorator)
- 装饰模式(Decorator)
- DECORATOR (装饰)模式
- Decorator(装饰)模式
- 装饰模式(Decorator)
- Decorator(装饰模式)
- 装饰模式(Decorator)
- 装饰模式(Decorator)
- 装饰模式(Decorator)
- Decorator(装饰)模式
- 装饰模式(Decorator)
- java基础-循环
- Java实现排序
- Docker Network Configuration 高级网络配置
- HDU 1027 Ignatius and the Princess II 选择序列题解
- 数据库中的索引
- 装饰模式(Decorator)与动态代理的强强联合
- c库函数
- 【Android】ListView与SimpleAdapter适配器的使用
- ZOJ 2760 How Many Shortest Path(Dijistra + ISAP 最大流)
- 基于S3C2440芯片的蜂鸣器驱动开发
- HDFS常用的文件API操作
- Insert Interval @LeetCode
- 黑马程序员——Java基础__异常
- 【转】如何在_Linux_下调试动态链接库