装饰者模式Decorator
简单版
防止类爆炸
类继承是会造成耦合度很高
用聚合代替继承,和代理不一样的是主动权在谁手里,聚合是主动的,自己知道的,代理是被动,自己甚至不知道
- Component:抽象被装饰组件,定义都有哪些功能。
- ConcreteComponent:抽象被装饰组件实现类
- Decorator:抽象****装饰器,不一定是接口,它持有一个Component对象实例的引用,定义一个与抽象被装饰组件一致的接口;
- ConcreteDecorator:具体****装饰器,负责实现装饰器角色定义的功能。
详细版
我们在做一个产品的时候,可能并没有考虑到新需求的场景,此时就需要为某些组件添加新的功能来满足这些需求。
- 如果要符合开放-封闭的原则,我们最好不要直接修改已有的具体实现类,因为会破坏其已有的稳定性。
- 继承,在某些场景下是不可行的,例如,要覆盖的方法被 final 关键字修饰了,那么在 Java 的语法中就无法被覆盖。使用继承方案的另一个缺点就是整个继承树的膨胀,例如,当新需求存在多种排列组合或是复杂的判断时,那就需要写非常多的子类实现。
- 应该尽量多地使用组合方式进行扩展,尽量少使用继承方式进行扩展,除非迫不得已。装饰器模式就是一种通过组合方式实现扩展的设计模式,它可以完美地解决上述功能增强的问题。装饰器的核心思想是为已有实现类创建多个包装类,由这些新增的包装类完成新需求的扩展。相较于继承这种静态的扩展方式,装饰器模式可以在运行时根据系统状态,动态决定为一个实现类添加哪些扩展功能。
- Component 接口:已有的业务接口,JDK 中的 IO 流体系就使用了装饰器模式,其中的 InputStream 接口就扮演了 Component 接口的角色。
- ComponentImpl 实现类:实现了 Component 接口最基础、最核心的功能,也就是被装饰的、原始的基础类。在 JDK IO 流体系之中的 FileInputStream 就扮演了 ComponentImpl 的角色,它实现了读取文件的基本能力,例如,读取单个 byte、读取 byte[] 数组。
- Decorator 抽象类:所有装饰器的父类,核心不是提供新的扩展能力,而是封装一个 Component 类型的字段,也就是被装饰的目标对象。
- 这个被装饰的对象可以是 ComponentImpl 对象,也可以是 Decorator 实现类的对象,之所以这么设计,就是为了实现装饰器嵌套。这里的 DecoratorImpl1 装饰了 DecoratorImpl2,DecoratorImpl2 装饰了 ComponentImpl,经过了这一系列装饰之后得到的 Component 对象,除了具有 ComponentImpl 的基础能力之外,还拥有了 DecoratorImpl1 和 DecoratorImpl2 的扩展能力。JDK IO 流体系中的 FilterInputStream 就扮演了 Decorator 的角色。
- Decorator 与 Component 的引用关系:Decorator 的具体实现类核心就是在被装饰对象的基础之上添加新的扩展功能。在 JDK IO 流体系中的 BufferedInputStream 就扮演了 DecoratorImpl 的角色,它在原有的 InputStream 基础上,添加了一个 byte[] 缓冲区,提供了更加高效的读文件操作。
- 这个被装饰的对象可以是 ComponentImpl 对象,也可以是 Decorator 实现类的对象,之所以这么设计,就是为了实现装饰器嵌套。这里的 DecoratorImpl1 装饰了 DecoratorImpl2,DecoratorImpl2 装饰了 ComponentImpl,经过了这一系列装饰之后得到的 Component 对象,除了具有 ComponentImpl 的基础能力之外,还拥有了 DecoratorImpl1 和 DecoratorImpl2 的扩展能力。JDK IO 流体系中的 FilterInputStream 就扮演了 Decorator 的角色。
应用InputStream,OutputStream,Reader,Writer
在由InputStream,OutputStream,Reader和Writer代表的等级结构内部,有一些流处理器可以 对另一些流处理器起到装饰作用,形成新的,具有改善了的功能的流处理器。
FilterInputStream继承了InputStream,也引用了InputStream,而它有四个子类,这就是所谓的Decorator模式。
BufferedInputStream“装饰”了InputStream的内部工作方式,使得流的读入操作使用了缓冲机制。在使用了缓冲机制后,不会对每一次的流读入操作都产生一个物理的读盘动作,从而提高了程序的效率,在汲及到物理流的读入时,都应当使用这个装饰流类。
package com.deltaqin.designPattern.d12_decorator;
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
/**
* @author deltaqin
* @date 2021/3/27 1:38 下午
*/
public class Demo {
public static void main(String[] args) throws FileNotFoundException,
IOException {
FileInputStream fis = null;
// 不使用装饰器BufferedInputStream
fis = new FileInputStream("./test.data");
long t = System.currentTimeMillis();
int c;
while ((c = fis.read()) != -1) {
}
t = System.currentTimeMillis() - t;
System.out.println("遍历文件用了如下时间:" + t);
fis.close();
// 使用装饰器BufferedInputStream
FileInputStream fis1 = new FileInputStream("./test.data");
BufferedInputStream bis = new BufferedInputStream(fis1);
t = System.currentTimeMillis();
while ((c = bis.read()) != -1) {}
fis1.close();
t = System.currentTimeMillis() - t;
System.out.println("遍历文件用了如下时间:" + t);
fis1.close();
bis.close();
}
}
LineNumberInputStream和PushbackInputStream也同样“装饰”了InputStream的内部工作方式,前者使得程序能够按照行号读入数据;后者能够使程序读入的过程中,退后一个字符。
Java语言的I/O库提供了四大等级结构: InputStream,OutputStream,Reader,Writer 四个系列的类。InputStream和OutputStream处理8位字节流数据, Reader和Writer处理16位的字符流数据。InputStream和Reader处理输入, OutputStream和Writer处理输出,所以OutputStream,Reader,Writer这三类的装饰模式跟前面详细介绍的InputStream装饰模式大同小异
装饰者与适配器区别
适配器模式的意义是将一个接口转变成另一个接口,通过改变接口达到重复使用的目的;适配器****重点在于转换
装饰器模式不是要改变被装饰对象的接口,而恰恰要保持原有的接口,但增强原有对象的功能,或者改变原有对象的处理方法而提高性能。所有这两个模式的设计目的是不同的。装饰器****重点在于增强
posted on 2025-10-14 23:10 chuchengzhi 阅读(3) 评论(0) 收藏 举报