设计模式-装饰器模式

设计模式-装饰器模式

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

案例分析

一般说 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();
    }

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

相关推荐
木鹅.30 分钟前
LangChain4j
java
永远都不秃头的程序员(互关)43 分钟前
Java核心技术精要:高效实践指南
java·开发语言·性能优化
CoderYanger1 小时前
动态规划算法-子序列问题(数组中不连续的一段):28.摆动序列
java·算法·leetcode·动态规划·1024程序员节
代码栈上的思考1 小时前
深入解析Spring IoC核心与关键注解
java·后端·spring
Mai Dang1 小时前
SpringBoot4用Swagger
java
geekmice3 小时前
实现一个功能:springboot项目启动将controller地址拼接打印到txt文件
java·spring boot·后端
蓝瑟3 小时前
告别重复造轮子!业务组件多场景复用实战指南
前端·javascript·设计模式
老华带你飞3 小时前
旅游|基于Java旅游信息系统(源码+数据库+文档)
java·开发语言·数据库·vue.js·spring boot·旅游
小石头 100863 小时前
【JavaEE】进程和线程的区别
java·java-ee