装饰器模式实战指南:动态增强Java对象的能力

449 阅读17分钟

1. 什么是装饰器模式?

1.1 一句话定义

装饰器模式 是一种结构型设计模式,允许通过包装(装饰)对象的方式,动态添加新功能,且不修改原有类代码
(类比现实:给手机贴膜、加保护壳,不影响手机本体功能,但能叠加新特性)


1.2 核心思想

图解:装饰器如何工作

flowchart LR
    Component[基础对象] --> Decorator[装饰器]
    Decorator --> ConcreteDecoratorA[具体装饰器A]
    Decorator --> ConcreteDecoratorB[具体装饰器B]

逐层解析

  1. 基础对象:提供核心功能(如一杯纯咖啡)
  2. 装饰器:继承基础对象的接口,持有其引用(如咖啡杯的“容器”)
  3. 具体装饰器:在调用基础对象前后添加逻辑(如加糖、加牛奶)

代码结构示意

// 基础接口(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 对比继承的直观理解

继承方式:刚性扩展

// 每增加一种组合就要创建新子类
class 加糖咖啡 extends 咖啡 {}
class 加奶咖啡 extends 咖啡 {}
class 加糖加奶咖啡 extends 咖啡 {} // 类数量爆炸!

装饰器模式:灵活组合

// 自由组合装饰器
Drink drink = new 加糖装饰器(
              new 加奶装饰器(
              new 咖啡()));

1.5 现实中的装饰器模式

现实案例对应角色Java代码映射
快递包裹基础对象 + 装饰器包裹 = new 加固包装(new 快递())
照片滤镜原始图片 + 滤镜装饰图片 = new 美颜滤镜(new 原始图())
游戏角色装备系统角色本体 + 装备装饰角色 = new 武器(new 护甲(new 玩家()))

2. 核心结构与实现步骤

2.1 UML类图解析(手把手拆解)

类图详解

classDiagram
    class Component {
        <<interface>>   # 标记为接口
        +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.io.InputStream)
public interface FileSystem {
    String read();       // 读取文件
    void write(String data); // 写入文件
}

第二步:实现具体组件(ConcreteComponent)

// 基础文件系统实现(类比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)

// 装饰器基类(类比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)

// 加密装饰器(类比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("压缩后的数据: ", "");
    }
}

客户端使用示例

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 装饰器模式三要素

  1. 相同的接口:装饰器与被装饰对象实现相同接口
    (否则无法实现透明装饰,客户端不知道被装饰对象的存在)
  2. 持有组件引用:装饰器内部必须持有组件对象的引用
    (通过构造函数注入,形成包装链)
  3. 可嵌套组合:装饰器本身也可以被其他装饰器装饰
    (形成多层嵌套结构,类似new A(new B(new C()))

2.4 与继承的关键区别

场景:给文本添加格式

// 继承方式:每个组合都需要子类
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 场景一:咖啡订单系统(避免类爆炸)

传统继承方式的问题

// 每增加一种配料组合都要创建新子类
class 加糖咖啡 extends 咖啡 {}
class 加奶咖啡 extends 咖啡 {}
class 加糖加奶咖啡 extends 咖啡 {} // 类数量呈指数级增长

装饰器解决方案(附完整可运行代码)

// 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装饰器体系

// 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中间件(拦截器链实现)

中间件装饰器架构

// 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 场景扩展:游戏装备系统(理解多层装饰)

装备组合示例代码

// 基础角色接口
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 经典案例对比分析

案例:实现带日志和缓存的文件读取

继承方案痛点

// 需要为每种组合创建子类
class 带日志的文件读取器 extends 基础文件读取器 {}
class 带缓存的文件读取器 extends 基础文件读取器 {}
class 带日志和缓存的文件读取器 extends 基础文件读取器 {} // 冗余代码!

装饰器方案优势

// 任意组合现有装饰器
FileReader reader = new 缓存装饰器(
                   new 日志装饰器(
                   new 基础文件读取器()));

// 可随时调整组合
if (isDebugMode) {
    reader = new 日志装饰器(reader); // 运行时动态添加
}

4.4 什么时候绝对不要用装饰器?

  1. 功能有严格顺序要求

    // 错误示例:装饰顺序影响结果
    Coffee c1 = new 加糖装饰器(new 加奶装饰器(coffee)); // 糖+奶
    Coffee c2 = new 加奶装饰器(new 加糖装饰器(coffee)); // 奶+糖
    // 两者结果可能不同,但客户端无法感知
    
  2. 需要访问私有成员

    class 基础类 {
        private int secret; // 装饰器无法直接访问
    }
    
    class 装饰器 extends 基础类 {
        void 方法() {
            // 无法访问secret字段!
        }
    }
    
  3. 性能敏感场景

    // 高频调用场景(如每秒百万次)
    public void 高频方法() {
        // 经过10层装饰器调用
    }
    // 累计的方法调用栈开销不可忽视
    

4.5 混合使用技巧

案例:Spring Security的FilterChain

// 继承体系定义基础功能
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()));

通过这个对比章节,读者可以明确:

  1. 何时应该放弃继承选择装饰器
  2. 如何通过流程图快速决策
  3. 装饰器的适用边界与风险点
  4. 在实际框架中如何混合使用两种方式

5. 注意事项与常见误区

5.1 避免过度装饰(附真实故障案例)

错误示例:失控的装饰链

flowchart LR
    原始请求 --> 日志装饰器 --> 缓存装饰器 --> 加密装饰器 --> 压缩装饰器 --> 限流装饰器 --> 业务处理
    业务处理 --> 响应包装装饰器 --> 结果格式化装饰器 --> 最终响应

导致的问题

  1. 调试困难:异常栈跟踪会显示10+层调用
  2. 性能损耗:每个装饰器增加0.5ms延迟,10层即5ms
  3. 理解成本高:新成员需要画图才能理清流程

真实事故案例

某电商系统在促销期间添加了过多装饰器:

  • 价格计算装饰链:基础价格 -> 会员折扣 -> 满减 -> 优惠券 -> 限时补贴 -> 运费计算
  • 结果:计算延迟从20ms飙升到150ms,导致结算页超时

解决方案:将固定优惠策略合并到基础类,仅保留动态策略使用装饰器


5.2 三大使用原则(代码级详解)

原则一:装饰顺序敏感

// 加密装饰器和压缩装饰器的顺序差异
DataSource ds1 = new EncryptionDecorator( // 先加密后压缩
                 new CompressionDecorator(
                 new FileDataSource()));
// 加密后的二进制数据更易压缩

DataSource ds2 = new CompressionDecorator( // 先压缩后加密
                 new EncryptionDecorator(
                 new FileDataSource()));
// 压缩后的数据加密可能导致体积更大

原则二:接口一致性(反面教材)

// 错误示例:装饰器接口不兼容
class 高级咖啡机 {
    void 制作咖啡() { /* 新方法 */ }
}

class 加糖装饰器 extends 高级咖啡机 { // 违反接口一致性
    private 咖啡机 wrappee;
    
    // 缺少原接口的getCost()等方法
}

// 客户端调用时发现方法缺失!

原则三:控制装饰层数

// 超过3层建议重构
Beverage drink = new 冰块装饰器(
                 new 糖浆装饰器(
                 new 奶油装饰器(
                 new 杯型升级装饰器(  // 第4层,考虑拆分
                 new 基础奶茶()))));

// 重构方案:合并固定组合
class 豪华套餐装饰器 extends 装饰器基类 {
    public 豪华套餐装饰器(Beverage drink) {
        super(new 糖浆装饰器(
             new 奶油装饰器(
             new 杯型升级装饰器(drink))));
    }
}

5.3 性能考量(含优化方案)

性能测试对比

装饰层数单次调用耗时(纳秒)百万次调用总耗时(秒)
0(原始对象)1200.12
3层装饰3600.36
5层装饰6000.60
10层装饰12001.20

优化技巧

  1. 缓存装饰结果

    class 缓存装饰器 implements DataSource {
        private DataSource wrappee;
        private String cache;
        
        public String read() {
            if (cache == null) {
                cache = wrappee.read(); // 只读一次
            }
            return cache;
        }
    }
    
  2. 并行处理装饰逻辑

    class 并行加密装饰器 implements DataSource {
        public void write(String data) {
            CompletableFuture.runAsync(() -> {
                String encrypted = encrypt(data);
                wrappee.write(encrypted); // 异步写入
            });
        }
    }
    
  3. 使用静态代理替代动态装饰

    // 合并高频装饰逻辑
    class 高性能装饰器 implements DataSource {
        private final DataSource wrappee;
        
        public String read() {
            // 直接内联加密+压缩逻辑
            String data = wrappee.read();
            return decrypt(decompress(data));
        }
    }
    

5.4 易被忽略的线程安全问题

错误场景

class 计数装饰器 implements HttpHandler {
    private int requestCount; // 状态字段
    private HttpHandler wrappee;
    
    public void handle(Request req) {
        requestCount++; // 多线程下可能出错
        wrappee.handle(req);
    }
}

// 多线程调用时requestCount统计不准确

解决方案

  1. 使用原子变量

    private AtomicInteger requestCount = new AtomicInteger();
    public void handle(Request req) {
        requestCount.incrementAndGet();
        // ...
    }
    
  2. 装饰器无状态化

    class 无状态计数装饰器 implements HttpHandler {
        private final HttpHandler wrappee;
        
        public void handle(Request req) {
            GlobalCounter.increment(); // 委托给外部线程安全组件
            wrappee.handle(req);
        }
    }
    

5.5 初始化复杂度的应对策略

问题:多层装饰导致创建语句冗长

DataSource ds = new A(
               new B(
               new C(
               new D(
               new E(source)))));

解决方案:使用建造者模式封装

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 项目实战建议(附可复用模板)

步骤一:识别变化点

// 示例:电商订单处理流程中的可变点
public class OrderProcessor {
    // 固定流程(无需装饰)
    private void saveToDB() { /* 数据库存储 */ }
    
    // 需要动态扩展的环节(装饰点)
    public void validate() { /* 基础校验 */ }     // 可装饰:添加风控校验
    public void calculatePrice() { /* 价格计算 */ } // 可装饰:添加促销策略
    public void notifyUser() { /* 通知用户 */ }    // 可装饰:切换通知渠道
}

步骤二:定义标准化接口

// 装饰器接口模板
public interface OrderProcessorDecorator {
    void beforeValidate();      // 前置增强
    void afterCalculatePrice(); // 后置增强
    void aroundNotify();        // 环绕增强
}

// 具体装饰器实现示例
public class RiskControlDecorator implements OrderProcessorDecorator {
    public void beforeValidate() {
        System.out.println("[风控] 检查用户信用分...");
    }
}

步骤三:编写单元测试

// 测试装饰器组合效果
@Test
void testDecoratorChain() {
    OrderProcessor processor = new OrderProcessor();
    
    // 构造装饰链
    processor = new DiscountDecorator(
               new RiskControlDecorator(
               new SMSNotifyDecorator(processor)));
               
    processor.processOrder();
    
    assertThat(processor.getLogs())
        .contains("风控检查", "折扣计算", "短信通知");
}

步骤四:文档注释规范

/**
 * 促销折扣装饰器
 * 
 * <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 {
        <<abstract>>
        +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 通过装饰添加缓冲功能
  • 通过嵌套实现多功能组合:
    InputStream 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过滤功能

  1. 在断点设置中添加条件:this instanceof ConcreteDecorator
  2. 使用 Frames窗口 查看装饰器调用栈

Q:装饰器能否替代继承?
A:不能完全替代,但能解决这些问题:

  • 多维度扩展咖啡口味(加糖/加奶/加奶油)
  • 动态组合文件处理功能(加密+压缩+缓存)
  • 增强不可修改的第三方库类

通过这个实战指南,开发者可以:

  1. 快速判断何时使用装饰器模式
  2. 通过标准化模板快速落地实施
  3. 借鉴经典框架的设计思想
  4. 获得持续学习的明确路径