设计模式-装饰器模式

设计模式-装饰器模式

装饰器故名思义就是对原有的对象进行装饰,但与代理模式不同,代理模式是对功能进行业务无关的补充,而装饰器模式则是对原有对象功能的增强。

案例分析

一般说 Spring 的事务是动态代理,在事务执行前获取 sqlSession 首先关闭 autoCommit,执行事务操作,然后 commit,这些其实与真正的保存操作是没有关系的。

java 复制代码
        try {
            session.setAutoCommit(false);
            save();
            commit();
        } catch (Exception e) {
            rollback();
        } finally {
            session.close;
        }

但装饰器模式则更加强调对原有功能的增强,例如 Java 中某个文件读取只能每次读取 1 个字符,读取文件是需要 IO 操作的,会比较慢;如果有一片缓冲区,可以读取一定长度到缓存区,这样下次读字符时可以直接从缓冲区读取,这就是对字符型文件读取的增强。

java 复制代码
// BufferedInputStream 源码
public class BufferedInputStream extends FilterInputStream {
    private static int DEFAULT_BUFFER_SIZE = 8192;
    protected volatile byte[] buf;

    public BufferedInputStream(InputStream in, int size) {
        super(in);
        if (size <= 0) {
            throw new IllegalArgumentException("Buffer size <= 0");
        }
        buf = new byte[size];
    }
}    

但是 Java IO 使用的时候看起来可能有一点奇怪:

java 复制代码
        InputStream in = new FileInputStream("/");
        BufferedInputStream inputStream = new BufferedInputStream(in);

需要先创建一个 FileInputStream 并传入 BufferedInputStream 中,那么为什么需要这样呢,InputStream 是一个抽象类,为什么不利用继承直接使用 InputStream 引用呢?就像下面这样

java 复制代码
InputStream in = new BufferedInputStream("/");

如果这么实现,那么 BufferedInputStream 提供的是缓冲区读取功能,如果还有其他的功能,也需要继承 InputStream 类。如果一个新的类既支持缓冲区读取,有需要其他功能,就需要连续继承,这会使类之间的关系复杂且难以理解。

另一方面,使用这种组合的方式还有好处,如果一个类既需要支持 A 功能,有需要支持缓冲区读取,可以使用如下的代码:

java 复制代码
        InputStream in = new FileInputStream("/");
        BufferedInputStream inputStream = new BufferedInputStream(in);
		// 二次装饰 二次增强
        DataInputStream stream = new DataInputStream(inputStream);

所有的 IO 读取类都继承 InputStream ,这样可以利用多态进行多重增强。

不过你如果实际去看这些 IO 类的源码,它们并非直接继承了 InputStream,而是继承了 FilterInputStream,InputStream 类中其实关于文件读取有默认实现,正常来讲继承的时候只需要对需要增强对地方增强,其他地方使用默认实现即可,但是对于文件操作来讲,默认实现是拿不到对于文件的真实句柄的,自然也无法进行读写,因此需要传入真正的 inputStream 操作文件。

例如 buffered 只针对缓存进行增强,如果需要关闭文件流,其实这个功能是不需要增强的,但是直接调用父类的方法拿不到文件句柄,就像下面这样,父类拿不到文件句柄自然也无法实现关闭操作

java 复制代码
    public void close() throws IOException {
        super.close();
    }

因此 Java IO 体系中设计了 FilterInputStream,在创建具体子类实例(例如 BufferedInputStream)时需要传入一个真正的 InputStream 对象,这个对象会被赋值在 FilterInputStream 中的属性中,这样子类在调用这些无需增强的方法时就不需要重写了。

java 复制代码
    protected FilterInputStream(InputStream in) {
        this.in = in;
    }  

	public void close() throws IOException {
        in.close();
    }

装饰器模式主要解决继承关系过于复杂的问题,通过组合来替代继承。它主要的作用是给原始类添加增强功能。这也是判断是否该用装饰器模式的一个重要的依据。除此之外,装饰器模式还有一个特点,那就是可以对原始类嵌套使用多个装饰器。为了满足这个应用场景,在设计的时候,装饰器类需要跟原始类继承相同的抽象类或者实现相同接口。

相关推荐
洛_尘1 天前
JAVA EE初阶 2: 多线程-初阶
java·开发语言
Slow菜鸟1 天前
Java 开发环境安装指南(五) | Git 安装
java·git
lkbhua莱克瓦241 天前
Java基础——方法
java·开发语言·笔记·github·学习方法
q***71851 天前
海康威视摄像头ISUP(原EHOME协议) 摄像头实时预览springboot 版本java实现,并可以在浏览器vue前端播放(附带源码)
java·前端·spring boot
_Jimmy_1 天前
JUC包里的同步组件主要实现了AQS的哪些主要方法
java
范纹杉想快点毕业1 天前
《嵌入式开发硬核指南:91问一次讲透底层到架构》
java·开发语言·数据库·单片机·嵌入式硬件·mongodb
大G的笔记本1 天前
Java常见设计模式面试题(高频)
java·开发语言·设计模式
老鼠只爱大米1 天前
Java设计模式之建造者模式(Builder)详解
java·设计模式·建造者模式·builder·23种设计模式
笃行客从不躺平1 天前
线程池原理复习
java·开发语言
weixin_448771721 天前
SpringMVC执行流程源码分析之二
java