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

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

👋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):添加具体增强功能

【案例】订单系统 - 改
image.png

组件接口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 实现类型安全
目录
相关文章
|
10月前
|
设计模式 存储 缓存
【设计模式】【结构型模式】享元模式(Flyweight)
一、入门 什么是享元模式? 享元模式(Flyweight Pattern)是一种结构型设计模式,旨在通过共享对象来减少内存使用,特别适用于存在大量相似对象的情况。 它的核心思想是将对象的内在状态(不变
339 16
|
10月前
|
设计模式 Java 数据库连接
【设计模式】【结构型模式】代理模式(Proxy)
一、入门 什么是代理模式? 代理模式(Proxy Pattern)是一种结构型设计模式,允许你提供一个代理对象来控制对另一个对象的访问。 代理对象在客户端和目标对象之间起到中介作用,可以在不改变目标对
305 10
|
10月前
|
设计模式 Java 定位技术
【设计模式】【结构型模式】组合模式(Composite)
一、入门 什么是组合模式 组合模式(Composite Pattern)是一种结构型设计模式,它允许你将对象组合成树形结构来表示“部分-整体”的层次关系。组合模式使得客户端可以统一处理单个对象和组合对
329 10
|
10月前
|
关系型数据库 Java MySQL
【设计模式】【结构型模式】桥接模式(Bridge)
一、入门 什么是桥接模式? 桥接模式(Bridge Pattern)是一种结构型设计模式,核心思想是将抽象与实现分离,让它们可以独立变化。简单来说,它像一座“桥”连接了两个维度的变化,避免用继承导致代
497 10
|
10月前
|
设计模式 前端开发 Java
【设计模式】【结构型模式】适配器模式(Adpter)
一、入门 什么是适配器模式? 适配器模式是Java中常用的结构型设计模式,它的核心作用就像现实中的电源转换器一样---让原本不兼容的两个接口能够协同工作。 为什么要用适配器模式? 假设我们需要在电商系
265 10
|
10月前
|
设计模式 Java 数据库连接
【设计模式】【结构型模式】外观模式(Facde)
一、入门 什么是外观模式? 一种结构型设计模式,通过为子系统中的一组接口提供一个统一的高层接口(称为外观),来简化客户端与复杂子系统的交互过程。其本质是建立抽象层来隔离复杂度。 为什么要有外观模式?
380 9
|
设计模式 Java 数据安全/隐私保护
Java 设计模式:装饰者模式(Decorator Pattern)
装饰者模式属于结构型设计模式,允许通过动态包装对象的方式为对象添加新功能,提供比继承更灵活的扩展方式。该模式通过组合替代继承,遵循开闭原则(对扩展开放,对修改关闭)。
|
设计模式 Java Kotlin
Kotlin教程笔记(56) - 改良设计模式 - 装饰者模式
Kotlin教程笔记(56) - 改良设计模式 - 装饰者模式
189 2
|
10月前
|
设计模式 Java 数据库连接
【设计模式】【创建型模式】工厂方法模式(Factory Methods)
一、入门 什么是工厂方法模式? 工厂方法模式(Factory Method Pattern)是一种创建型设计模式,它定义了一个用于创建对象的接口,但由子类决定实例化哪个类。工厂方法模式使类的实例化延迟
302 16