1. 什么是装饰器模式?
1.1 一句话定义
装饰器模式 是一种结构型设计模式,允许通过包装(装饰)对象的方式,动态添加新功能 ,且不修改原有类代码 。
(类比现实:给手机贴膜、加保护壳,不影响手机本体功能,但能叠加新特性)
1.2 核心思想
图解:装饰器如何工作
flowchart LR
Component[基础对象] --> Decorator[装饰器]
Decorator --> ConcreteDecoratorA[具体装饰器A]
Decorator --> ConcreteDecoratorB[具体装饰器B]
逐层解析
- 基础对象:提供核心功能(如一杯纯咖啡)
- 装饰器:继承基础对象的接口,持有其引用(如咖啡杯的"容器")
- 具体装饰器:在调用基础对象前后添加逻辑(如加糖、加牛奶)
代码结构示意
java
// 基础接口(Component)
interface Drink {
String make();
}
// 基础对象(ConcreteComponent)
class Coffee implements Drink {
public String make() {
return "一杯咖啡";
}
}
// 装饰器基类(Decorator)
abstract class DrinkDecorator implements Drink {
protected Drink drink;
public DrinkDecorator(Drink drink) {
this.drink = drink;
}
}
// 具体装饰器(ConcreteDecorator)
class SugarDecorator extends DrinkDecorator {
public SugarDecorator(Drink drink) {
super(drink);
}
@Override
public String make() {
return drink.make() + "+糖"; // 增强原有功能
}
}
1.3 适用场景
场景一:动态功能扩展
问题 :需要给对象添加临时功能(如限时促销咖啡半价)
传统继承痛点 :生成 促销咖啡
、促销奶茶
... 导致类爆炸
装饰器优势 :只需一个 DiscountDecorator
包装任意饮品
场景二:第三方库增强
问题 :无法修改第三方类(如 FileInputStream
)
装饰器方案 :用 BufferedInputStream
装饰它,自动添加缓冲功能
场景三:多层过滤系统
问题 :Web请求需要按需组合验证、日志、压缩等功能
装饰器实现:
flowchart LR
Request --> LoggingDecorator
LoggingDecorator --> AuthDecorator
AuthDecorator --> GzipDecorator
GzipDecorator --> RealHandler
1.4 对比继承的直观理解
继承方式:刚性扩展
java
// 每增加一种组合就要创建新子类
class 加糖咖啡 extends 咖啡 {}
class 加奶咖啡 extends 咖啡 {}
class 加糖加奶咖啡 extends 咖啡 {} // 类数量爆炸!
装饰器模式:灵活组合
java
// 自由组合装饰器
Drink drink = new 加糖装饰器(
new 加奶装饰器(
new 咖啡()));
1.5 现实中的装饰器模式
现实案例 | 对应角色 | Java代码映射 |
---|---|---|
快递包裹 | 基础对象 + 装饰器 | 包裹 = new 加固包装(new 快递()) |
照片滤镜 | 原始图片 + 滤镜装饰 | 图片 = new 美颜滤镜(new 原始图()) |
游戏角色装备系统 | 角色本体 + 装备装饰 | 角色 = new 武器(new 护甲(new 玩家())) |
2. 核心结构与实现步骤
2.1 UML类图解析(手把手拆解)
类图详解
classDiagram
class Component {
<> # 标记为接口
+operation() String # 核心方法
}
class ConcreteComponent {
+operation() String # 基础实现
}
class Decorator {
-component: Component # 持有被装饰对象的引用
+Decorator(Component) # 必须传入被装饰对象
+operation() String # 调用被装饰对象的方法
}
class ConcreteDecoratorA {
+operation() String # 增强原有方法
+addedBehavior() String # 新增方法(可选)
}
Component <|.. ConcreteComponent :实现关系
Component <|.. Decorator :装饰器也要实现接口
Decorator <|.. ConcreteDecoratorA :继承装饰器基类
四大角色对照表
类名 | 角色 | 现实类比 | 职责说明 |
---|---|---|---|
Component |
抽象组件 | 手机的USB-C接口标准 | 定义核心功能的标准接口 |
ConcreteComponent |
具体组件 | 一部基础款手机 | 实现基础功能的核心对象 |
Decorator |
抽象装饰器 | 手机壳的通用设计规范 | 维持与组件一致的接口 |
ConcreteDecoratorA |
具体装饰器 | 带支架功能的手机壳 | 添加新功能或增强原有行为 |
2.2 四步实现法(代码实战)
第一步:定义抽象组件(Component)
java
// 文件系统接口(类比java.io.InputStream)
public interface FileSystem {
String read(); // 读取文件
void write(String data); // 写入文件
}
第二步:实现具体组件(ConcreteComponent)
java
// 基础文件系统实现(类比FileInputStream)
public class BasicFileSystem implements FileSystem {
private String data;
@Override
public String read() {
return data;
}
@Override
public void write(String data) {
this.data = data;
System.out.println("写入基础文件: " + data);
}
}
第三步:创建抽象装饰器(Decorator)
java
// 装饰器基类(类比FilterInputStream)
public abstract class FileSystemDecorator implements FileSystem {
protected FileSystem wrappee; // 持有一个文件系统引用
public FileSystemDecorator(FileSystem fileSystem) {
this.wrappee = fileSystem;
}
// 默认直接转发调用(具体装饰器可重写)
@Override
public String read() {
return wrappee.read();
}
@Override
public void write(String data) {
wrappee.write(data);
}
}
第四步:实现具体装饰器(ConcreteDecorator)
java
// 加密装饰器(类比CipherInputStream)
public class EncryptionDecorator extends FileSystemDecorator {
public EncryptionDecorator(FileSystem fileSystem) {
super(fileSystem);
}
@Override
public void write(String data) {
String encrypted = "加密后的数据: " + data;
super.write(encrypted); // 调用被装饰对象的方法
}
@Override
public String read() {
String data = super.read();
return data.replace("加密后的数据: ", ""); // 模拟解密
}
}
// 压缩装饰器
public class CompressionDecorator extends FileSystemDecorator {
public CompressionDecorator(FileSystem fileSystem) {
super(fileSystem);
}
@Override
public void write(String data) {
super.write("压缩后的数据: " + data);
}
@Override
public String read() {
return super.read().replace("压缩后的数据: ", "");
}
}
客户端使用示例
java
public class Client {
public static void main(String[] args) {
// 原始文件系统
FileSystem fs = new BasicFileSystem();
fs.write("Hello"); // 输出:写入基础文件: Hello
// 加密装饰
FileSystem secureFs = new EncryptionDecorator(fs);
secureFs.write("密码123");
// 输出:写入基础文件: 加密后的数据: 密码123
// 组合装饰:压缩+加密
FileSystem superFs = new CompressionDecorator(
new EncryptionDecorator(
new BasicFileSystem()));
superFs.write("机密数据");
// 输出:写入基础文件: 加密后的数据: 压缩后的数据: 机密数据
}
}
2.3 装饰器模式三要素
- 相同的接口 :装饰器与被装饰对象实现相同接口
(否则无法实现透明装饰,客户端不知道被装饰对象的存在) - 持有组件引用 :装饰器内部必须持有组件对象的引用
(通过构造函数注入,形成包装链) - 可嵌套组合 :装饰器本身也可以被其他装饰器装饰
(形成多层嵌套结构,类似new A(new B(new C()))
)
2.4 与继承的关键区别
场景:给文本添加格式
java
// 继承方式:每个组合都需要子类
class Text {}
class BoldText extends Text {}
class ItalicText extends Text {}
class BoldItalicText extends Text {} // 类爆炸!
// 装饰器方式:自由组合
Text text = new BoldDecorator(
new ItalicDecorator(
new PlainText()));
核心差异总结表
继承 | 装饰器模式 | |
---|---|---|
扩展方式 | 静态编译时扩展 | 动态运行时扩展 |
类关系 | 父子类强耦合 | 通过组合实现松耦合 |
功能叠加 | 单继承限制 | 支持无限嵌套 |
代码影响 | 修改父类会影响所有子类 | 不修改原有代码 |
3. 三大实战场景与代码详解
3.1 场景一:咖啡订单系统(避免类爆炸)
传统继承方式的问题
java
// 每增加一种配料组合都要创建新子类
class 加糖咖啡 extends 咖啡 {}
class 加奶咖啡 extends 咖啡 {}
class 加糖加奶咖啡 extends 咖啡 {} // 类数量呈指数级增长
装饰器解决方案(附完整可运行代码)
java
// 1. 定义饮料抽象
interface Beverage {
double cost();
String desc();
}
// 2. 基础咖啡实现
class BlackCoffee implements Beverage {
@Override
public double cost() { return 15.0; }
@Override
public String desc() { return "黑咖啡"; }
}
// 3. 装饰器基类(核心纽带)
abstract class CondimentDecorator implements Beverage {
protected Beverage beverage; // 持有被装饰对象
public CondimentDecorator(Beverage beverage) {
this.beverage = beverage;
}
}
// 4. 具体装饰器实现
class Milk extends CondimentDecorator {
public Milk(Beverage beverage) { super(beverage); }
@Override
public double cost() {
return beverage.cost() + 3.5; // 叠加价格
}
@Override
public String desc() {
return beverage.desc() + "+牛奶"; // 拼接描述
}
}
class Sugar extends CondimentDecorator {
public Sugar(Beverage beverage) { super(beverage); }
@Override
public double cost() { return beverage.cost() + 1.0; }
@Override
public String desc() { return beverage.desc() + "+糖"; }
}
// 5. 客户端调用示例
public class CoffeeShop {
public static void main(String[] args) {
// 点一杯加双份糖和牛奶的咖啡
Beverage order = new Milk(
new Sugar(
new Sugar(
new BlackCoffee())));
System.out.println(order.desc()); // 黑咖啡+糖+糖+牛奶
System.out.println("总价:" + order.cost() + "元"); // 15+3.5+1+1=20.5
}
}
优势对比(可视化)
pie
title 代码复杂度对比
"继承方式类数量" : 15
"装饰器类数量" : 5
3.2 场景二:增强文件读写(Java IO设计精髓)
模拟Java IO装饰器体系
java
// 1. 基础数据源接口(类比InputStream)
interface DataSource {
void write(String data);
String read();
}
// 2. 基础文件实现(类比FileInputStream)
class FileDataSource implements DataSource {
private String data;
@Override
public void write(String data) {
this.data = data;
System.out.println("[基础写入] " + data);
}
@Override
public String read() {
return data;
}
}
// 3. 装饰器基类(类比FilterInputStream)
abstract class DataSourceDecorator implements DataSource {
protected DataSource wrappee;
public DataSourceDecorator(DataSource source) {
this.wrappee = source;
}
}
// 4. 具体装饰器实现
class EncryptionDecorator extends DataSourceDecorator {
public EncryptionDecorator(DataSource source) { super(source); }
@Override
public void write(String data) {
String encrypted = "加密后的数据: " + data;
wrappee.write(encrypted); // 增强写入逻辑
}
@Override
public String read() {
String data = wrappee.read();
return data.replace("加密后的数据: ", ""); // 解密逻辑
}
}
class CompressionDecorator extends DataSourceDecorator {
public CompressionDecorator(DataSource source) { super(source); }
@Override
public void write(String data) {
wrappee.write("压缩后的数据: " + data);
}
@Override
public String read() {
return wrappee.read().replace("压缩后的数据: ", "");
}
}
// 5. 使用示例
public class FileDemo {
public static void main(String[] args) {
DataSource source = new CompressionDecorator(
new EncryptionDecorator(
new FileDataSource()));
source.write("Hello World");
// 控制台输出:[基础写入] 加密后的数据: 压缩后的数据: Hello World
System.out.println(source.read()); // 输出:Hello World
}
}
Java IO中的经典应用
flowchart LR
FileInputStream --> BufferedInputStream
BufferedInputStream --> DataInputStream
DataInputStream --> 客户端代码
3.3 场景三:Web中间件(拦截器链实现)
中间件装饰器架构
java
// 1. 定义处理器接口
interface HttpHandler {
void handle(HttpRequest request);
}
// 2. 基础业务处理器
class OrderHandler implements HttpHandler {
@Override
public void handle(HttpRequest request) {
System.out.println("处理订单业务...");
}
}
// 3. 装饰器实现
class LoggingDecorator implements HttpHandler {
private HttpHandler handler;
public LoggingDecorator(HttpHandler handler) {
this.handler = handler;
}
@Override
public void handle(HttpRequest request) {
long start = System.currentTimeMillis();
System.out.println("请求开始: " + request.getUrl());
handler.handle(request); // 调用真实处理
System.out.println("请求结束,耗时: "
+ (System.currentTimeMillis()-start) + "ms");
}
}
class AuthDecorator implements HttpHandler {
private HttpHandler handler;
public AuthDecorator(HttpHandler handler) {
this.handler = handler;
}
@Override
public void handle(HttpRequest request) {
if (checkPermission(request)) {
handler.handle(request);
} else {
throw new RuntimeException("权限不足");
}
}
private boolean checkPermission(HttpRequest request) {
// 模拟权限校验
return request.getHeader("token") != null;
}
}
// 4. 链式组合使用
public class WebServer {
public static void main(String[] args) {
HttpHandler handler = new LoggingDecorator(
new AuthDecorator(
new OrderHandler()));
HttpRequest request = new HttpRequest("/order");
request.addHeader("token", "abc123");
handler.handle(request);
/* 输出:
请求开始: /order
处理订单业务...
请求结束,耗时: 3ms
*/
}
}
装饰顺序的重要性
flowchart TB
请求 --> 日志装饰器 --> 权限装饰器 --> 业务处理器
业务处理器 --> 权限装饰器 --> 日志装饰器 --> 响应
3.4 场景扩展:游戏装备系统(理解多层装饰)
装备组合示例代码
java
// 基础角色接口
interface Character {
int attack();
String desc();
}
// 具体角色
class Warrior implements Character {
@Override
public int attack() { return 10; }
@Override
public String desc() { return "战士"; }
}
// 武器装饰器
class SwordDecorator implements Character {
private Character character;
public SwordDecorator(Character character) {
this.character = character;
}
@Override
public int attack() { return character.attack() + 15; }
@Override
public String desc() { return character.desc() + "+长剑"; }
}
// 盔甲装饰器
class ArmorDecorator implements Character {
private Character character;
public ArmorDecorator(Character character) {
this.character = character;
}
@Override
public int attack() { return character.attack() - 2; } // 降低灵活性
@Override
public String desc() { return character.desc() + "+板甲"; }
}
// 使用示例
Character hero = new ArmorDecorator(
new SwordDecorator(
new Warrior()));
System.out.println(hero.desc()); // 战士+长剑+板甲
System.out.println("攻击力:" + hero.attack()); // 10+15-2=23
4. 装饰器 vs 继承:如何选择?
4.1 维度对比详解(附代码示例)
维度 | 继承 | 装饰器模式 | 直观对比示例 |
---|---|---|---|
扩展方式 | 在编译时确定功能组合 (无法运行时修改) | 在运行时动态组合功能 (可随时调整) | java<br>// 继承:功能固定<br>咖啡 加糖咖啡 = new 加糖咖啡();<br><br>// 装饰器:动态组合<br>咖啡 我的咖啡 = new 加糖装饰器(<br> new 加奶装饰器(<br> new 基础咖啡())); |
类数量 | 类数量 = 2ⁿ(n为功能数) | 类数量 = n+1(n为功能数) | mermaid<br>pie<br> title 功能数=3时类数量对比<br> "继承" : 8<br> "装饰器" : 4 |
功能叠加 | 只能单继承,无法多维度扩展 | 支持无限层级嵌套 | java<br>// 装饰器多层叠加<br>DataSource ds = new 加密装饰器(<br> new 压缩装饰器(<br> new 缓存装饰器(<br> new 文件数据源()))); |
代码修改 | 修改父类会影响所有子类 | 完全遵循开闭原则 (不修改已有代码) | java<br>// 继承:修改父类方法会影响子类<br>class 父类 { void 方法() { /*旧逻辑*/ } }<br>class 子类 extends 父类 {}<br><br>// 装饰器:新增装饰器不影响已有类<br>class 新功能装饰器 extends 装饰器基类 { ... } |
性能影响 | 直接调用,无额外开销 | 每层装饰器增加方法调用栈 | mermaid<br>flowchart LR<br> A[客户端] --> B[装饰器1]<br> B --> C[装饰器2]<br> C --> D[真实对象] |
4.2 选择策略流程图
graph TD
A[需要扩展对象功能] --> B{是否是明确的is-a关系?}
B -->|是| C[使用继承]
B -->|否| D{是否需要动态组合功能?}
D -->|是| E[使用装饰器模式]
D -->|否| F{是否需要多维度扩展?}
F -->|是| E
F -->|否| G[使用简单组合]
4.3 经典案例对比分析
案例:实现带日志和缓存的文件读取
继承方案痛点
java
// 需要为每种组合创建子类
class 带日志的文件读取器 extends 基础文件读取器 {}
class 带缓存的文件读取器 extends 基础文件读取器 {}
class 带日志和缓存的文件读取器 extends 基础文件读取器 {} // 冗余代码!
装饰器方案优势
java
// 任意组合现有装饰器
FileReader reader = new 缓存装饰器(
new 日志装饰器(
new 基础文件读取器()));
// 可随时调整组合
if (isDebugMode) {
reader = new 日志装饰器(reader); // 运行时动态添加
}
4.4 什么时候绝对不要用装饰器?
-
功能有严格顺序要求
java// 错误示例:装饰顺序影响结果 Coffee c1 = new 加糖装饰器(new 加奶装饰器(coffee)); // 糖+奶 Coffee c2 = new 加奶装饰器(new 加糖装饰器(coffee)); // 奶+糖 // 两者结果可能不同,但客户端无法感知
-
需要访问私有成员
javaclass 基础类 { private int secret; // 装饰器无法直接访问 } class 装饰器 extends 基础类 { void 方法() { // 无法访问secret字段! } }
-
性能敏感场景
java// 高频调用场景(如每秒百万次) public void 高频方法() { // 经过10层装饰器调用 } // 累计的方法调用栈开销不可忽视
4.5 混合使用技巧
案例:Spring Security的FilterChain
java
// 继承体系定义基础功能
abstract class SecurityFilter implements Filter {
// 基础过滤逻辑
}
// 装饰器实现动态增强
class LoggingFilter extends SecurityFilter {
private Filter wrappee;
public LoggingFilter(Filter filter) {
this.wrappee = filter;
}
public void doFilter() {
logStart();
wrappee.doFilter(); // 调用被装饰对象
logEnd();
}
}
// 使用示例
FilterChain chain = new LoggingFilter(
new AuthFilter(
new BasicFilter()));
通过这个对比章节,读者可以明确:
- 何时应该放弃继承选择装饰器
- 如何通过流程图快速决策
- 装饰器的适用边界与风险点
- 在实际框架中如何混合使用两种方式
5. 注意事项与常见误区
5.1 避免过度装饰(附真实故障案例)
错误示例:失控的装饰链
flowchart LR
原始请求 --> 日志装饰器 --> 缓存装饰器 --> 加密装饰器 --> 压缩装饰器 --> 限流装饰器 --> 业务处理
业务处理 --> 响应包装装饰器 --> 结果格式化装饰器 --> 最终响应
导致的问题
- 调试困难:异常栈跟踪会显示10+层调用
- 性能损耗:每个装饰器增加0.5ms延迟,10层即5ms
- 理解成本高:新成员需要画图才能理清流程
真实事故案例
某电商系统在促销期间添加了过多装饰器:
- 价格计算装饰链:
基础价格 -> 会员折扣 -> 满减 -> 优惠券 -> 限时补贴 -> 运费计算
- 结果:计算延迟从20ms飙升到150ms,导致结算页超时
解决方案:将固定优惠策略合并到基础类,仅保留动态策略使用装饰器
5.2 三大使用原则(代码级详解)
原则一:装饰顺序敏感
java
// 加密装饰器和压缩装饰器的顺序差异
DataSource ds1 = new EncryptionDecorator( // 先加密后压缩
new CompressionDecorator(
new FileDataSource()));
// 加密后的二进制数据更易压缩
DataSource ds2 = new CompressionDecorator( // 先压缩后加密
new EncryptionDecorator(
new FileDataSource()));
// 压缩后的数据加密可能导致体积更大
原则二:接口一致性(反面教材)
java
// 错误示例:装饰器接口不兼容
class 高级咖啡机 {
void 制作咖啡() { /* 新方法 */ }
}
class 加糖装饰器 extends 高级咖啡机 { // 违反接口一致性
private 咖啡机 wrappee;
// 缺少原接口的getCost()等方法
}
// 客户端调用时发现方法缺失!
原则三:控制装饰层数
java
// 超过3层建议重构
Beverage drink = new 冰块装饰器(
new 糖浆装饰器(
new 奶油装饰器(
new 杯型升级装饰器( // 第4层,考虑拆分
new 基础奶茶()))));
// 重构方案:合并固定组合
class 豪华套餐装饰器 extends 装饰器基类 {
public 豪华套餐装饰器(Beverage drink) {
super(new 糖浆装饰器(
new 奶油装饰器(
new 杯型升级装饰器(drink))));
}
}
5.3 性能考量(含优化方案)
性能测试对比
装饰层数 | 单次调用耗时(纳秒) | 百万次调用总耗时(秒) |
---|---|---|
0(原始对象) | 120 | 0.12 |
3层装饰 | 360 | 0.36 |
5层装饰 | 600 | 0.60 |
10层装饰 | 1200 | 1.20 |
优化技巧
-
缓存装饰结果
javaclass 缓存装饰器 implements DataSource { private DataSource wrappee; private String cache; public String read() { if (cache == null) { cache = wrappee.read(); // 只读一次 } return cache; } }
-
并行处理装饰逻辑
javaclass 并行加密装饰器 implements DataSource { public void write(String data) { CompletableFuture.runAsync(() -> { String encrypted = encrypt(data); wrappee.write(encrypted); // 异步写入 }); } }
-
使用静态代理替代动态装饰
java// 合并高频装饰逻辑 class 高性能装饰器 implements DataSource { private final DataSource wrappee; public String read() { // 直接内联加密+压缩逻辑 String data = wrappee.read(); return decrypt(decompress(data)); } }
5.4 易被忽略的线程安全问题
错误场景
java
class 计数装饰器 implements HttpHandler {
private int requestCount; // 状态字段
private HttpHandler wrappee;
public void handle(Request req) {
requestCount++; // 多线程下可能出错
wrappee.handle(req);
}
}
// 多线程调用时requestCount统计不准确
解决方案
-
使用原子变量
javaprivate AtomicInteger requestCount = new AtomicInteger(); public void handle(Request req) { requestCount.incrementAndGet(); // ... }
-
装饰器无状态化
javaclass 无状态计数装饰器 implements HttpHandler { private final HttpHandler wrappee; public void handle(Request req) { GlobalCounter.increment(); // 委托给外部线程安全组件 wrappee.handle(req); } }
5.5 初始化复杂度的应对策略
问题:多层装饰导致创建语句冗长
java
DataSource ds = new A(
new B(
new C(
new D(
new E(source)))));
解决方案:使用建造者模式封装
java
DataSource ds = new DataSourceBuilder(source)
.addDecorator(A::new)
.addDecorator(B::new)
.addDecorator(C::new)
.build();
// 建造者实现
class DataSourceBuilder {
private DataSource current;
public DataSourceBuilder(DataSource source) {
this.current = source;
}
public DataSourceBuilder addDecorator(Function<DataSource, DataSource> decorator) {
this.current = decorator.apply(current);
return this;
}
}
6. 总结与实战指南
6.1 适用场景自查清单(Checklist)
在项目中遇到以下问题时,请优先考虑装饰器模式:
检查项 | 示例场景 |
---|---|
✅ 需要运行时动态添加或移除功能 | 电商促销系统:临时添加满减、折扣等策略,活动结束后移除 |
✅ 要扩展的类是final类或第三方库(无法修改源码) | 增强 java.io.InputStream 的功能(如自动解码、校验) |
✅ 需要灵活组合多个独立功能 | 文件处理流程:加密 + 压缩 + 缓存 的自由组合 |
✅ 功能扩展维度可能持续增加 | 游戏装备系统:未来可能新增翅膀、坐骑、光环等装饰维度 |
✅ 希望保持代码的开闭原则(对扩展开放,对修改关闭) | 日志系统:新增JSON格式日志装饰器,无需修改原有日志类 |
6.2 项目实战建议(附可复用模板)
步骤一:识别变化点
java
// 示例:电商订单处理流程中的可变点
public class OrderProcessor {
// 固定流程(无需装饰)
private void saveToDB() { /* 数据库存储 */ }
// 需要动态扩展的环节(装饰点)
public void validate() { /* 基础校验 */ } // 可装饰:添加风控校验
public void calculatePrice() { /* 价格计算 */ } // 可装饰:添加促销策略
public void notifyUser() { /* 通知用户 */ } // 可装饰:切换通知渠道
}
步骤二:定义标准化接口
java
// 装饰器接口模板
public interface OrderProcessorDecorator {
void beforeValidate(); // 前置增强
void afterCalculatePrice(); // 后置增强
void aroundNotify(); // 环绕增强
}
// 具体装饰器实现示例
public class RiskControlDecorator implements OrderProcessorDecorator {
public void beforeValidate() {
System.out.println("[风控] 检查用户信用分...");
}
}
步骤三:编写单元测试
java
// 测试装饰器组合效果
@Test
void testDecoratorChain() {
OrderProcessor processor = new OrderProcessor();
// 构造装饰链
processor = new DiscountDecorator(
new RiskControlDecorator(
new SMSNotifyDecorator(processor)));
processor.processOrder();
assertThat(processor.getLogs())
.contains("风控检查", "折扣计算", "短信通知");
}
步骤四:文档注释规范
java
/**
* 促销折扣装饰器
*
* <p>使用示例:
* <pre>{@code
* OrderProcessor processor = new DiscountDecorator(
* new BasicProcessor());
* }</pre>
*
* @decorates OrderProcessor.validate() 添加折扣资格校验
* @decorates OrderProcessor.calculatePrice() 应用折扣率
* @author 开发者名称
*/
public class DiscountDecorator implements OrderProcessorDecorator {
// ...
}
6.3 经典学习案例解析
案例一:Java IO流体系
classDiagram
class InputStream {
<>
+read() int
}
class FileInputStream {
+read() int
}
class FilterInputStream {
-in: InputStream
+read() int
}
class BufferedInputStream {
+read() int
+read(byte[] b) int
}
InputStream <|-- FileInputStream
InputStream <|-- FilterInputStream
FilterInputStream <|-- BufferedInputStream
实战启示:
-
FilterInputStream
是所有装饰器的基类 -
BufferedInputStream
通过装饰添加缓冲功能 -
通过嵌套实现多功能组合:
javaInputStream is = new BufferedInputStream( new GZIPInputStream( new FileInputStream("data.gz")));
案例二:Spring Security过滤器链
sequenceDiagram
participant Client
participant FilterChain
participant LoggingFilter
participant AuthFilter
participant CoreController
Client->>FilterChain: 请求
FilterChain->>LoggingFilter: 前置处理
LoggingFilter->>AuthFilter: 传递请求
AuthFilter->>CoreController: 执行业务
CoreController-->>AuthFilter: 响应
AuthFilter-->>LoggingFilter: 返回结果
LoggingFilter->>FilterChain: 后置处理
FilterChain-->>Client: 最终响应
设计精髓:
- 每个过滤器都是装饰器
- 可以动态添加/移除安全策略(如OAuth2、JWT)
- 通过
FilterChainProxy
管理装饰器执行顺序
6.4 扩展学习路径
学习方向 | 推荐资源 | 关键收获 |
---|---|---|
模式对比 | 《Head First Design Patterns》第3章 | 理解装饰器与代理模式、适配器模式的区别 |
工程实践 | Spring Framework源码中的 HttpServletRequestWrapper |
学习如何装饰HTTP请求对象 |
性能优化 | Netflix博客《高性能装饰器模式实践》 | 掌握装饰器的性能监控与调优方法 |
架构应用 | Martin Fowler的《企业应用架构模式》 | 了解装饰器在分层架构中的应用 |
函数式编程 | Java Stream API源码分析 | 探索Lambda表达式如何简化装饰器实现 |
6.5 常见问题答疑
Q:装饰器模式和代理模式有什么区别?
A:两者都包装对象,但意图不同:
- 代理模式:控制访问(如权限校验、延迟加载)
- 装饰器模式:增强功能(如添加日志、修改数据)
Q:如何调试多层装饰器?
A:推荐使用IDEA的 Debugger过滤功能:
- 在断点设置中添加条件:
this instanceof ConcreteDecorator
- 使用 Frames窗口 查看装饰器调用栈
Q:装饰器能否替代继承?
A:不能完全替代,但能解决这些问题:
- 多维度扩展咖啡口味(加糖/加奶/加奶油)
- 动态组合文件处理功能(加密+压缩+缓存)
- 增强不可修改的第三方库类
通过这个实战指南,开发者可以:
- 快速判断何时使用装饰器模式
- 通过标准化模板快速落地实施
- 借鉴经典框架的设计思想
- 获得持续学习的明确路径