Bootstrap

【设计模式】【结构型模式】装饰者模式(Decorator)

👋hi,我不是一名外包公司的员工,也不会偷吃茶水间的零食,我的梦想是能写高端CRUD
🔥 2025本人正在沉淀中… 博客更新速度++
👍 欢迎点赞、收藏、关注,跟上我的更新节奏
🎵 当你的天空突然下了大雨,那是我在为你炸乌云

一、入门

什么是装饰者模式?

装饰者模式(Decorator Pattern)是 Java 中常用的结构型设计模式,它能在不修改原有对象结构的前提下,动态地为对象添加额外的职责。

为什么要装饰者模式?

假设你正在开发一个电商系统,需要处理订单的 基础价格 和 多种附加服务(如快递加急、礼品包装、保价服务等),且这些附加服务可以任意组合。
下面是没有用装饰者模式的代码实现:

// 基础订单
class BasicOrder { /* 计算基础价格 */ }

// 各种组合继承(产生大量重复代码)
class BasicOrderWithExpress extends BasicOrder {}       // 基础+快递
class BasicOrderWithGiftWrap extends BasicOrder {}     // 基础+礼品
class BasicOrderWithExpressAndGiftWrap extends BasicOrder {} // 基础+快递+礼品
// ...更多组合(n种附加服务会产生2^n个子类!)

存在问题

  1. 类数量指数级增长:3种附加服务需要7个子类,4种需要15个…
  2. 代码重复严重:每个子类都要重写价格计算方法
  3. 难以维护:新增一个附加服务(如保价),需要修改所有相关子类
  4. 无法动态组合:运行时不能灵活增减服务

怎样实现装饰者模式?

装饰者模式由以下构成

  1. 组件接口(Component):定义核心功能的抽象。定义被装饰对象和装饰器的共同行为规范,声明核心业务方法(如价格计算、服务描述),确保装饰过程的透明性(客户端无需区分原始对象和装饰后的对象)
  2. 具体组件(ConcreteComponent):基础实现类。实现组件接口的基础功能,作为装饰过程的原始起点(最内层的被装饰对象),不应包含任何装饰逻辑
  3. 装饰器基类(Decorator):持有组件引用,实现组件接口
  4. 具体装饰器(ConcreteDecorator):添加具体增强功能

【案例】订单系统 - 改
在这里插入图片描述

组件接口Order接口

interface Order {
    double calculatePrice();
    String getDescription();
}

具体组件:基础订单,BasicOrder

class BasicOrder implements Order {
    public double calculatePrice() { return 100.0; } // 基础价格100元
    public String getDescription() { return "基础订单"; }
}

装饰器基类OrderDecorator

// 装饰器基类(关键:实现相同接口)
abstract class OrderDecorator implements Order {
    protected Order decoratedOrder;

    public OrderDecorator(Order order) {
        this.decoratedOrder = order;
    }

    // 默认直接转发给被装饰对象
    public double calculatePrice() {
        return decoratedOrder.calculatePrice();
    }

    public String getDescription() {
        return decoratedOrder.getDescription();
    }
}

具体装饰器ExpressDecorator类(快递加急)、GiftWrapDecorator类(礼品包装)、InsuranceDecorator类(保价服务)

// 具体装饰器:快递加急
class ExpressDecorator extends OrderDecorator {
    public ExpressDecorator(Order order) {
        super(order);
    }

    public double calculatePrice() {
        return super.calculatePrice() + 25.0; // 加急费25元
    }

    public String getDescription() {
        return super.getDescription() + " + 快递加急";
    }
}

// 具体装饰器:礼品包装
class GiftWrapDecorator extends OrderDecorator {
    public GiftWrapDecorator(Order order) {
        super(order);
    }

    public double calculatePrice() {
        return super.calculatePrice() + 15.0; // 包装费15元
    }

    public String getDescription() {
        return super.getDescription() + " + 礼品包装";
    }
}

// 具体装饰器:保价服务
class InsuranceDecorator extends OrderDecorator {
    public InsuranceDecorator(Order order) {
        super(order);
    }

    public double calculatePrice() {
        return super.calculatePrice() + 30.0; // 保价费30元
    }

    public String getDescription() {
        return super.getDescription() + " + 保价服务";
    }
}

客户端使用

public class Client {
    public static void main(String[] args) {
        // 基础订单
        Order order = new BasicOrder();
        
        // 动态叠加服务(可任意组合)
        order = new ExpressDecorator(order);     // 加急
        order = new GiftWrapDecorator(order);   // 包装
        order = new InsuranceDecorator(order);  // 保价

        System.out.println(order.getDescription());
        // 输出:基础订单 + 快递加急 + 礼品包装 + 保价服务
        
        System.out.println("总价:" + order.calculatePrice());
        // 输出:总价:170.0
    }
}

二、装饰者模式在框架源码中的实现

Java I/O 流体系(最经典实现)

InputStream in = new BufferedInputStream( // 具体装饰器
                   new FileInputStream( // 具体组件
                       new File("test.txt")));

源码中的实现
组件接口(Component)InputStream抽象类。
具体组件(ConcreteComponent)FileInputStream类。
装饰器基类(Decorator)FilterInputStream类。
具体装饰器(ConcreteDecorator)BufferedInputStream类。

// 装饰器基类 FilterInputStream
public class FilterInputStream extends InputStream {
    protected volatile InputStream in; // 持有被装饰对象

    protected FilterInputStream(InputStream in) {
        this.in = in;
    }

    // 默认转发所有方法到被装饰对象
    public int read() throws IOException {
        return in.read();
    }
}

// 具体装饰器 BufferedInputStream
public class BufferedInputStream extends FilterInputStream {
    private byte[] buf; // 新增缓冲区状态

    public BufferedInputStream(InputStream in) {
        super(in); // 必须传入被装饰对象
        buf = new byte[8192];
    }

    // 增强的读取方法(实现缓冲机制)
    public int read() throws IOException {
        if (pos >= count) {
            fill(); // 装饰器新增的核心逻辑
            if (pos >= count)
                return -1;
        }
        return getBufIfOpen()[pos++] & 0xff;
    }
}

Spring 框架中的装饰者应用

这里说的比较简单,后续我学到Spring源码再补充😁
典型场景:事务缓存装饰器

// 装饰器基类实现
public class TransactionAwareCacheDecorator implements Cache {
    private final Cache targetCache; // 被装饰的缓存对象

    public TransactionAwareCacheDecorator(Cache targetCache) {
        this.targetCache = targetCache;
    }

    // 在事务提交后才执行实际put操作
    public void put(final Object key, final Object value) {
        TransactionSynchronizationManager.registerSynchronization(
            new TransactionSynchronizationAdapter() {
                public void afterCommit() {
                    targetCache.put(key, value);
                }
            });
    }
}
  • 装饰器注册:通过 CacheManager 包装原始缓存
  • 事务感知:延迟缓存操作到事务提交后
  • 与代理模式结合:常通过 AOP 动态应用装饰器

三、总结

装饰者模式的优点

  1. 动态扩展功能
    • 无需修改原有代码,运行时动态添加功能
    • 案例Java I/O 流中,BufferedInputStream 动态为 FileInputStream 添加缓冲功能
  2. 避免类爆炸
    • 相比继承,装饰者模式通过组合实现功能扩展,避免子类数量指数级增长
    • 案例:电商订单系统中,3种附加服务只需3个装饰类,而不是7个子类
  3. 符合开闭原则
    • 对扩展开放:新增装饰器不影响现有代码
    • 对修改封闭:无需修改组件接口和具体组件
    • 案例:Spring 的 TransactionAwareCacheDecorator 扩展缓存功能,不影响原有缓存实现
  4. 灵活组合
    • 装饰器可以任意组合,实现不同功能叠加
    • 案例:Netty 的 ChannelPipeline 中,多个 ChannelHandler 可以按需组合
  5. 透明性
    • 客户端无需区分原始对象和装饰后的对象
    • 案例Java CollectionsunmodifiableList 返回相同接口类型,对客户端透明

装饰者模式的缺点

  1. 复杂性增加
    • 多层装饰可能导致调用链过长,增加调试和理解难度
    • 案例Java I/O流中,多层装饰(如缓冲+字符集转换)可能让调用栈变深
  2. 装饰顺序敏感
    • 不同装饰顺序可能导致不同结果
    • 案例:电商订单系统中,先加急再包装 vs 先包装再加急,价格计算可能不同
  3. 小对象增多
    • 每个装饰器都是一个独立对象,可能增加内存开销
    • 案例:Netty 中,每个 ChannelHandler 都是一个独立装饰器
  4. 接口膨胀
    • 组件接口需要定义所有装饰器可能用到的方法
    • 案例:Java I/O 中,InputStream 需要定义所有可能的流操作方法
  5. 不适合静态配置
    • 如果功能组合是固定的,装饰者模式可能不如直接继承简洁
    • 案例:某些业务场景中,功能组合是固定的,直接继承更合适

装饰者模式的适用场景

  1. 动态功能扩展
    • 需要运行时动态添加或撤销功能
    • 案例:Java I/O 流、电商订单附加服务
  2. 避免子类爆炸
    • 功能组合复杂,继承会导致子类数量激增
    • 案例:游戏装备系统(武器+宝石+附魔)
  3. 透明增强
    • 需要在不改变客户端代码的情况下增强功能
    • 案例:Spring 的 TransactionAwareCacheDecorator
  4. 分层处理
    • 需要将功能拆分为多个独立模块,按需组合
    • 案例:Netty 的 ChannelPipeline、Web 中间件(权限校验→日志记录→缓存处理)
  5. 兼容性处理
    • 需要在不修改原有代码的情况下适配新功能
    • 案例:Java Collections 的 checkedList 实现类型安全
;