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

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

文章目录

  • 一、入门
    • 什么是装饰者模式?
    • 为什么要装饰者模式?
    • 怎样实现装饰者模式?
  • 二、装饰者模式在框架源码中的实现
    • Java I/O 流体系(最经典实现)
    • Spring 框架中的装饰者应用
  • 三、总结
    • 装饰者模式的优点
    • 装饰者模式的缺点
    • 装饰者模式的适用场景

一、入门

什么是装饰者模式?

装饰者模式(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 实现类型安全

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/971228.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

基于Ubuntu+vLLM+NVIDIA T4高效部署DeepSeek大模型实战指南

一、 前言:拥抱vLLM与T4显卡的强强联合 在探索人工智能的道路上,如何高效地部署和运行大型语言模型(LLMs)一直是一个核心挑战。尤其是当我们面对资源有限的环境时,这个问题变得更加突出。原始的DeepSeek-R1-32B模型虽…

Windows环境搭建ES集群

搭建步骤 下载安装包 下载链接:https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-7.17.27-windows-x86_64.zip 解压 解压并复制出3份 es-node1配置 config/elasticsearch.yml cluster.name: xixi-es-win node.name: node-1 path.data: D:\\wor…

STM32 I2C通信协议说明

目录 背景 I2C协议 数据的有效性 I2C通信开始和停止条件 I2C数据传输 发送 响应 正常情况: 异常情况: 主机结束接收 写寄存器的标准流程 读寄存器的标准流程 仲裁机制 时钟同步 SDA线的仲裁 程序 背景 对单片机的三大通信中的I2C通信进…

Unity学习part2

为bilibili教程【【Unity教程】零基础带你从小白到超神】 https://www.bilibili.com/video/BV1gQ4y1e7SS/?p50&share_sourcecopy_web&vd_source6e7a3cbb802eb986578ad26fae1eeaab的笔记 1、灯光的使用 定向光模拟太阳,是平行光。旋转定向光,光…

Vue 实现主题切换(明暗)

项目地址:https://gitee.com/abcdfdewrw/vue3_xiaohongshu_project 效果展示: 步骤1:定义明暗scss样式 // 浅色模式 html[data-theme"light"]:root {--header-height: 72px;--color-border-bottom: #eef2f9;--color-primary-lab…

rabbitmq五种模式的总结——附java-se实现(详细)

rabbitmq五种模式的总结 完整项目地址:https://github.com/9lucifer/rabbitmq4j-learning 一、简单模式 (一)简单模式概述 RabbitMQ 的简单模式是最基础的消息队列模式,包含以下两个角色: 生产者:负责发…

数据结构 day02

3. 线性表 3.1. 顺序表 3.1.3. 顺序表编程实现 操作:增删改查 .h 文件 #ifndef __SEQLIST_H__ #define __SEQLIST_H__ #define N 10 typedef struct seqlist {int data[N];int last; //代表数组中最后一个有效元素的下标 } seqlist_t;//1.创建一个空的顺序表 seq…

STM32的HAL库开发---ADC

一、ADC简介 1、ADC,全称:Analog-to-Digital Converter,指模拟/数字转换器 把一些传感器的物理量转换成电压,使用ADC采集电压,然后转换成数字量,经过单片机处理,进行控制和显示。 2、常见的AD…

25/2/16 <算法笔记> DirectPose

DirectPose 是一种直接从图像中预测物体的 6DoF(位姿:6 Degrees of Freedom)姿态 的方法,包括平移和平面旋转。它在目标检测、机器人视觉、增强现实(AR)和自动驾驶等领域中具有广泛应用。相比于传统的位姿估…

企业级API集成方案:基于阿里云函数计算调用DeepSeek全解析

解决方案链接:https://www.aliyun.com/solution/tech-solution/deepseek-r1-for-platforms?utm_contentg_1000401616 何为DeepSeek R1 DeepSeek R1模型有诸多技术优势。高效架构设计使其能更高效提取特征,减少冗余计算,提升数据处理速度、…

137,【4】 buuctf web [SCTF2019]Flag Shop

进入靶场 都点击看看 发现点击work会增加¥ 但肯定不能一直点下去 抓包看看 这看起来是一个 JWT(JSON Web Token)字符串。JWT 通常由三部分组成,通过点(.)分隔,分别是头部(Header&…

ThinkPHP8视图赋值与渲染

【图书介绍】《ThinkPHP 8高效构建Web应用》-CSDN博客 《2025新书 ThinkPHP 8高效构建Web应用 编程与应用开发丛书 夏磊 清华大学出版社教材书籍 9787302678236 ThinkPHP 8高效构建Web应用》【摘要 书评 试读】- 京东图书 在控制器操作中,使用view函数可以传入视图…

渗透利器:YAKIT 工具-基础实战教程.

YAKIT 工具-基础实战教程. YAKIT(Yak Integrated Toolkit)是一款基于Yak语言开发的集成化网络安全单兵工具,旨在覆盖渗透测试全流程,提供从信息收集、漏洞扫描到攻击实施的自动化支持。其核心目标是通过GUI界面降低Yak语言的使用…

Fiori APP配置中的Semantic object 小bug

在配置自开发程序的Fiori Tile时,需要填入Semantic Object。正常来说,是需要通过事务代码/N/UI2/SEMOBJ来提前新建的。 但是在S4 2022中,似乎存在一个bug,即无需新建也能输入自定义的Semantic Object。 如下,当我们任…

shell——分支语句

文章目录 基本语法常用判断条件(1)两个整数之间比较(2)按照文件权限进行判断(3)按照文件类型进行判断(4)多条件判断(&& 表示前一条命令执行成功时,才执行后一条命令&#xf…

Ubuntu 连接 air pods

1. sudo vim /etc/bluetooth/main.conf , 修改蓝牙模式为blder 2.sudo /etc/init.d/bluetooth restart, 重启蓝牙,即可连接成功

机器学习:k近邻

所有代码和文档均在golitter/Decoding-ML-Top10: 使用 Python 优雅地实现机器学习十大经典算法。 (github.com),欢迎查看。 K 邻近算法(K-Nearest Neighbors,简称 KNN)是一种经典的机器学习算法,主要用于分类和回归任务…

低空经济:开启未来空中生活的全新蓝海

引言 随着科技的进步,我们不再仅仅依赖地面交通和传统物流。你是否曾幻想过,未来的某一天,快递、外卖可以像魔法一样直接从空中送到你手中?或者,你能乘坐小型飞行器,快速穿梭于城市之间,告别拥堵…

DeepSeek核心算法解析:如何打造比肩ChatGPT的国产大模型

注:此文章内容均节选自充电了么创始人,CEO兼CTO陈敬雷老师的新书《自然语言处理原理与实战》(人工智能科学与技术丛书)【陈敬雷编著】【清华大学出版社】 文章目录 DeepSeek大模型技术系列一DeepSeek核心算法解析:如何…

苍穹外卖day4 redis相关简单知识 店铺营业状态设置

内存存储 键值对 key-value 一般用于处理突发性大量请求数据操作(暂时浅显理解) 读写速度极快,常用于缓存数据,减少对数据库的访问压力,提高系统性能。例如,可以缓存用户会话、商品信息、页面数据 设置默…