中 进阶
装饰器模式#
一句话答案#
动态给对象添加职责比继承更灵活,Java IO 流(BufferedReader 装饰 InputStreamReader)是典型应用。
核心要点
装饰器模式在不改变原始对象结构的情况下,动态地给对象添加新功能。装饰器和被装饰者实现相同的接口/继承相同的抽象类,装饰器内部持有被装饰对象的引用。
核心结构:
Component(抽象组件)
├── ConcreteComponent(被装饰的具体对象)
└── Decorator(装饰器抽象类,持有 Component 引用)
├── ConcreteDecoratorA
└── ConcreteDecoratorBplaintext装饰器 vs 继承:
| 维度 | 装饰器模式 | 继承 |
|---|---|---|
| 扩展方式 | 运行时动态组合 | 编译期静态确定 |
| 灵活性 | 可自由搭配多个装饰器 | 类爆炸(每种组合一个子类) |
| 开闭原则 | 遵守(新增装饰器即可) | 可能违反 |
Java IO 中的装饰器模式:
Java IO 是装饰器模式最经典的应用。InputStream 是抽象组件,FileInputStream 是具体组件,各种 FilterInputStream 子类是装饰器:
InputStream(抽象组件)
├── FileInputStream(具体组件:读文件)
├── ByteArrayInputStream(具体组件:读字节数组)
└── FilterInputStream(装饰器基类)
├── BufferedInputStream(增加缓冲功能)
├── DataInputStream(增加读取基本类型功能)
└── PushbackInputStream(增加回退功能)plaintext// 装饰器的层层包装——给 FileInputStream 添加缓冲 + 数据类型读取能力
InputStream is = new DataInputStream(
new BufferedInputStream(
new FileInputStream("data.bin")
)
);
// FileInputStream: 负责从文件读取原始字节
// BufferedInputStream: 装饰器,增加了缓冲区(默认 8KB)减少磁盘 IO
// DataInputStream: 装饰器,增加了 readInt()/readDouble() 等方法java关键区分——装饰器 vs 代理:
- 装饰器:增强功能,不改变接口语义(BufferedInputStream 加了缓冲但还是 InputStream)
- 代理:控制访问,可能加权限检查、远程调用等(AOP 代理控制方法调用)
面试回答(2分钟版)
装饰器模式的核心是在不修改原始对象的情况下动态地给它添加新功能,比继承更灵活。装饰器和被装饰对象实现相同的接口,装饰器内部持有被装饰对象的引用,在调用时先执行自己的增强逻辑再委托给被装饰对象。Java IO 是装饰器模式最经典的应用:FileInputStream 是具体组件负责读文件,BufferedInputStream 是装饰器给它加了缓冲功能,DataInputStream 又加了读取基本类型的功能,层层包装但本质还是 InputStream。和继承相比,装饰器的优势在于可以运行时动态组合——如果用继承实现 N 种功能的组合需要 2 的 N 次方个子类,而装饰器只需要 N 个装饰器类自由搭配。和代理模式的区别在于意图不同:装饰器是增强功能但不改变接口语义,比如加缓冲;代理是控制访问,比如权限检查、远程调用。装饰器可以多层嵌套,每层增加一种能力。
追问与易错
追问方向:
- “Java IO 中怎么体现?”→ InputStream 是抽象组件,FileInputStream 是具体组件,BufferedInputStream/DataInputStream 等 FilterInputStream 子类是装饰器,层层包装增加缓冲、类型读取等功能
- “和继承比优势?”→ 装饰器可运行时动态组合,N 种功能只需 N 个装饰器自由搭配;继承需要 2^N 个子类覆盖所有组合,会导致类爆炸,且编译期就固定无法动态调整
- “和代理关键区别?”→ 意图不同:装饰器是增强功能但不改变接口语义(如加缓冲仍是 InputStream);代理是控制访问(如权限检查、远程调用、AOP 事务拦截)
易错点:
- ❌ 装饰器和代理一样——意图不同增强 vs 控制
- ❌ 装饰器只能嵌套一层——可以多层