设计模式-装饰器模式

设计模式-装饰器模式

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

案例分析

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

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

相关推荐
wyzqhhhh21 分钟前
前端常见的设计模式
前端·设计模式
遥远_26 分钟前
电商履约大促峰值应对:核心业务数据预热方案详解
java·spring·1024程序员节·电商大促·数据预热
lemon_sjdk41 分钟前
每天学习一个新注解——@SafeVarargs
java
RoboWizard43 分钟前
电脑效能跃升利器 金士顿KVR内存焕新机
java·spring·智能手机·电脑·金士顿
微露清风1 小时前
系统性学习C++-第七讲-string类
java·c++·学习
m0_748233641 小时前
C++开发中的常用设计模式:深入解析与应用场景
javascript·c++·设计模式
spencer_tseng2 小时前
JDK 9 List.of(...)
java·windows·list·1024程序员节
Wind哥2 小时前
设计模式23种-C++实现
开发语言·c++·windows·设计模式
不平衡的叉叉树2 小时前
mybatis-plus的insertBatchSomeColumn方法实现批量插入
java·mybatis
学IT的周星星2 小时前
Maven 项目和 Maven Web 项目的异同点
java·maven