关于代理模式和装饰模式的一些思考

文章目录

    • 背景
    • 代理模式概述
    • 装饰模式概述
    • 编程启示录

背景

笔者前几天看了一个面试题 “说一下什么是代理模式?”,于是回忆了一下这个设计模式,并结合一篇旧文 《理理 Java 开发中常见的设计模式》,温故了一下这个知识点。

十年前看了好多遍的设计模式,死活处于学了忘、学不会的状态;如今随便翻到的某个 Java 技术点,都能快速反应出它们的前因后果。时光如梭,毕业已经十年了,这大概算是进入了卖油翁纯熟的技艺阶段了吧!

代理模式概述

代理模式类图。面向对象编程语言中,代理对象和委托对象都需要实现相同接口,同时代理对象关联一个真正的委托对象,客户端得到的是一个代理的引用,实则背后调用的是真正委托对象的方法:
关于代理模式和装饰模式的一些思考_第1张图片
代理类型。 对于有大量类需要代理的应用,这就是一种负担。一方面增加了工作量,而且还产生了大量相似的代理类,所以有了动态代理这个解决方案:不需要为每个类都创建一个代理类,只在需要使用代理的时候,通过反射机制动态地生成一个实现代理接口的匿名类的实例。代理的分类:动态代理和静态代理,即生成代理类的方式是什么。

动态代理的两种类型:JDKProxy 和 CGlib 两种,二者比对结果如下

类型 原理 特点
JDKProxy 运行时直接写Class字节码 生成代理类效率高,反射执行效率低
CodeGeneratorLibrary ASM框架写 Class 字节码 生成代理类效率低,FastClass 机制直接调用方法,执行效率高

Java 而言,ASM 就是字节码级别的编程,assembly ,即汇编语言。学过汇编的同学,有没有感觉很亲切呢?

JDKProxy。 JDK 是支持动态代理的,核心类为 Proxy
关于代理模式和装饰模式的一些思考_第2张图片
JDK 的 Proxy 实现动态代理,优势是,抽象统一的 InvocationHandler 实现类,完成公共的代理增强动作。然后参数是 targetInstance 。不需要定义 Proxy 实现类,直接通过动态代理生成代理类实例即可。

测试 JDKProxy 。根据这个类图,来编写一个动态代理的测试类如下。
第一步,定义代理接口类:

public interface Delegate {
	void log();
}

第二步,定义代理接口实现类:

public class DelegateImp implements Delegate{

	@Override
	public void log() {
		System.out.println("Class is "+this.getClass().getName());
	}
}

第三步,编写测试类:

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

public class ProxyTest {
	public static void main(String[] args) {
		//这里定义的是如何对委托类进行访问
		DelegateImp imp = new DelegateImp();
		InvocationHandler handler = new InvocationHandler() {
			// proxy 这个类是最终的代理类
			// 自定义的 InvocationHandler 需要指定具体的 target 目标对象的
			@Override
			public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
				//调用委托类的方法之前的操作
				System.out.println("Start to call "+method.getName()+",time is:"+new Date());
				//调用委托类的方法
				method.invoke(imp, args);
				//调用委托类之后的操作
				System.out.println("Finish to call "+method.getName()+",time is:"+new Date());
				return null;
			}
			
		};
		
		//创建一个代理类
		Delegate proxy = (Delegate)Proxy.newProxyInstance(DelegateImp.class.getClassLoader(), DelegateImp.class.getInterfaces(), handler);
		proxy.log();
	}
}

运行结果:log 方法执行前后,多了一下增强动作。

Start to call log,time is:Mon Jan 20 10:59:54 CST 2020
Class is javastudy.DelegateImp
Finish to call log,time is:Mon Jan 20 10:59:54 CST 2020

装饰模式概述

装饰模式指的是在不必改变原类文件和使用继承的情况下,动态地扩展一个对象的功能。它是通过创建一个包装对象,也就是装饰来包裹真实的对象。(来自百度百科的定义)

《Head first 设计模式》中描绘的该模式的静态结构图为:
关于代理模式和装饰模式的一些思考_第3张图片

  1. 具体组件类和装饰类必须实现相同的顶层接口 Component

  2. 具体装饰实现类包含一个具体组件的引用,即装饰类的构造过程需要传入一个具体的组件类,以便对原组件类进行装饰;

  3. 装饰类可以对被装饰者的行为增强,在之前或之后加上自己的动作。

编程启示录

仔细分析了一下,发现代理模式和装饰模式的区别有两点:
第一点,关联的类型不同。代理模式和装饰模式的区别,从类图结构上来看:

  • 代理关联的是具体的委托对象;装饰着装饰的是装饰者的接口类型;
  • 代理真正的委托对象,客户端是不知道的;装饰的具体对象,是由客户端来指定的。

第二点,目的不同。代理,着重是控制对委托对象的访问,由代理类本身决定谁是委托对象;装饰,是对被装饰对象的增强,由客户端指定对谁进行装饰。

另外,JDKProxy 动态代理的测试过程中,笔者想到的一些内容是:

JDKProxy 动态代理的核心是 InvokationHandler 的实现,它定义了如何对委托对象进行访问。如果在实际开发中,增强操作可以复用的话,这个接口的实现类并不会很多。但是,如果每个委托对象,都有自己的行为访问控制方式的话,那就跟静态代理没有什么区别了,因为需要为每个委托对象都创建一种 InvokationHandler 的实现类。
当然,就日常开发而言, InvokationHandler 基本上能够复用,实现类的数量不会太多。

己亥年农历腊月二十六,春节快到了,祝还在坚守岗位的朋友们,上班愉快……

你可能感兴趣的:(简单东西)