设计模式-装饰器模式

设计模式-装饰器模式

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

案例分析

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

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

相关推荐
Coder_Boy_3 小时前
技术让开发更轻松的底层矛盾
java·大数据·数据库·人工智能·深度学习
invicinble3 小时前
对tomcat的提供的功能与底层拓扑结构与实现机制的理解
java·tomcat
较真的菜鸟3 小时前
使用ASM和agent监控属性变化
java
黎雁·泠崖3 小时前
【魔法森林冒险】5/14 Allen类(三):任务进度与状态管理
java·开发语言
qq_12498707535 小时前
基于SSM的动物保护系统的设计与实现(源码+论文+部署+安装)
java·数据库·spring boot·毕业设计·ssm·计算机毕业设计
Coder_Boy_5 小时前
基于SpringAI的在线考试系统-考试系统开发流程案例
java·数据库·人工智能·spring boot·后端
Mr_sun.5 小时前
Day06——权限认证-项目集成
java
瑶山5 小时前
Spring Cloud微服务搭建四、集成RocketMQ消息队列
java·spring cloud·微服务·rocketmq·dashboard
abluckyboy5 小时前
Java 实现求 n 的 n^n 次方的最后一位数字
java·python·算法
2301_818732065 小时前
前端调用控制层接口,进不去,报错415,类型不匹配
java·spring boot·spring·tomcat·intellij-idea