装饰器模式:灵活扩展对象功能的利器

news2025/3/5 11:05:50

一、从咖啡加料说起:什么是装饰器模式?

假设您走进咖啡馆点单:

  • 基础款:美式咖啡(15元)
  • 加料需求:加牛奶(+3元)、加焦糖(+5元)、加奶油(+4元)

如果为每种组合创建子类,将出现类爆炸

Coffee
American
AmericanWithMilk
AmericanWithCaramel
AmericanWithMilkAndCaramel

装饰器模式(Decorator Pattern)
应运而生,通过动态包装对象的方式,实现功能的灵活扩展。


二、装饰器模式的核心结构

2.1 UML类图解析

Component
+operation()
ConcreteComponent
+operation()
Decorator
-component: Component
+operation()
ConcreteDecoratorA
+operation()
+addedBehavior()
ConcreteDecoratorB

2.2 关键角色说明

角色职责
Component定义基础功能接口
ConcreteComponent实现基础功能的具体组件
Decorator持有组件引用并实现相同接口
ConcreteDecorator具体装饰器,添加额外功能

三、装饰器模式实战:咖啡加料系统

3.1 基础组件定义

// 组件接口
public interface Coffee {
    String getDescription();
    double cost();
}

// 基础咖啡实现
public class American implements Coffee {
    @Override
    public String getDescription() {
        return "美式咖啡";
    }

    @Override
    public double cost() {
        return 15.0;
    }
}

3.2 抽象装饰器

public abstract class CoffeeDecorator implements Coffee {
    protected Coffee decoratedCoffee;

    public CoffeeDecorator(Coffee coffee) {
        this.decoratedCoffee = coffee;
    }

    @Override
    public String getDescription() {
        return decoratedCoffee.getDescription();
    }

    @Override
    public double cost() {
        return decoratedCoffee.cost();
    }
}

3.3 具体装饰器实现

// 牛奶装饰器
public class MilkDecorator extends CoffeeDecorator {
    public MilkDecorator(Coffee coffee) {
        super(coffee);
    }

    @Override
    public String getDescription() {
        return super.getDescription() + " + 牛奶";
    }

    @Override
    public double cost() {
        return super.cost() + 3.0;
    }
}

// 焦糖装饰器
public class CaramelDecorator extends CoffeeDecorator {
    public CaramelDecorator(Coffee coffee) {
        super(coffee);
    }

    @Override
    public String getDescription() {
        return super.getDescription() + " + 焦糖";
    }

    @Override
    public double cost() {
        return super.cost() + 5.0;
    }
}

3.4 客户端使用示例

public class CoffeeShop {
    public static void main(String[] args) {
        Coffee order1 = new American();
        System.out.println(order1.getDescription() + " 价格:" + order1.cost());

        Coffee order2 = new MilkDecorator(new American());
        System.out.println(order2.getDescription() + " 价格:" + order2.cost());

        Coffee order3 = new CaramelDecorator(new MilkDecorator(new American()));
        System.out.println(order3.getDescription() + " 价格:" + order3.cost());
    }
}

/* 输出:
美式咖啡 价格:15.0
美式咖啡 + 牛奶 价格:18.0
美式咖啡 + 牛奶 + 焦糖 价格:23.0
*/

四、装饰器模式的优势分析

4.1 与传统继承对比

维度继承方案装饰器模式
扩展方式静态编译期扩展动态运行时扩展
类数量组合爆炸(O(2^n))线性增长(O(n))
功能组合固定组合任意组合
维护成本修改父类影响所有子类独立扩展互不影响

4.2 核心优势总结

  1. 开闭原则:无需修改已有代码即可扩展功能
  2. 灵活组合:可以任意叠加装饰器
  3. 避免臃肿:将大类的功能分解为小装饰器
  4. 运行时扩展:动态增减对象功能

五、装饰器模式典型应用场景

5.1 Java IO流体系

// 多层装饰示例
InputStream input = new BufferedInputStream(
                    new GZIPInputStream(
                    new FileInputStream("data.gz")));

5.2 GUI组件装饰

JComponent textArea = new JScrollPane(
                     new BorderDecorator(
                     new ShadowDecorator(
                     new BasicTextArea())));

5.3 Web中间件开发

HttpServletRequest wrappedRequest = new LoggingRequestWrapper(
                                   new CachingRequestWrapper(
                                   originalRequest));

六、最佳实践与注意事项

6.1 实现建议

  1. 保持接口一致:装饰器必须实现组件接口
  2. 控制装饰层数:建议不超过5层装饰
  3. 明确文档说明:标注可组合的装饰器类型
  4. 性能监控:关注多层装饰的性能影响

6.2 常见误区

  • 滥用装饰器:简单扩展直接使用继承
  • 循环装饰:装饰器之间形成循环依赖
  • 状态管理:装饰器修改组件内部状态

七、与相关模式对比

模式核心区别
适配器模式改变接口,解决兼容性问题
代理模式控制访问,可能不透明
组合模式处理整体-部分层次结构
策略模式替换算法实现

八、总结:何时选择装饰器模式?

适用场景判断

  • ✅ 需要动态/透明地扩展对象功能
  • ✅ 不宜使用子类扩展(组合爆炸)
  • ✅ 需要撤销或修改已添加的功能

不适用场景

  • ❌ 组件接口频繁变化
  • ❌ 需要完全透明的对象(装饰器会改变类型)
  • ❌ 简单的一次性扩展需求

扩展阅读

  • 《设计模式:可复用面向对象软件的基础》第四章
  • Java I/O源码分析
  • Spring Web中的装饰器应用

掌握装饰器模式,让您的代码像乐高积木一样灵活组合! 🧱

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

网络原理--HTTP协议

http中文名为超文本传输协议,所谓“超文本”就是指传输范围超出了能在UTF8等码表上找到的字符的范围,包含一些图片,特殊格式之类的。 HTTP的发展简介 从图中可以看出到现在已经发展出了HTTP3,但是市面上的主流还是以HTTP1.0为主。…

华为hcia——Datacom实验指南——配置手工模式以太网链路聚合

什么是以太网链路聚合(Eth-trunk) 是一种将多个物理链路捆绑在一起,让设备以为是一条大链路,能够增加带宽,增加冗余度,提升可靠性,实现负载平衡。 传输方式有两种 基于数据流传输和基于数据包…

【C语言6】数组和函数实践:扫雷游戏的简单实现

文章目录 一、扫雷游戏分析和设计1.1 扫雷游戏的功能说明1.2 游戏的分析和设计1.2.1 数据结构的分析1.2.2 文件结构设计 二、扫雷游戏的代码实现三、扫雷游戏的扩展总结 一、扫雷游戏分析和设计 1.1 扫雷游戏的功能说明 使用控制台实现经典的扫雷游戏游戏可以通过菜单实现继续…

LeetCode 热题 100----1.两数之和

LeetCode 热题 100----1.两数之和 题目描述 我的解法 语言:js 思路就是:用双重循环去找哪两个数字相加等于target,目前的时间复杂度为O(n2),之后右优化思路再更新。

《模式和状态管理》知识总结三-EcuM与BswM模块的交互

前言 这篇文章主要搞清楚在模式管理中,BswM和EcuM各自的分工。距离学完模式管理也有几天时间了,写这篇文章算是复习一下。 EcuM及BswM交互总览 EcuM负责Ecu的上下电状态的处理,当Ecu处于正常运行状态的时候,EcuM会将Ecu的控制权…

客户需求模糊或频繁变更怎么办

应对客户需求模糊或频繁变更的关键在于 明确沟通、敏捷应对、科学决策。其中,明确沟通尤为重要,因为通过有效沟通,不仅能迅速厘清客户真实需求,还能及时发现隐藏问题,降低项目风险,为后续调整提供有力数据支…

动静态库-Linux 学习

在软件开发中,程序库是一组预先编写好的程序代码,它们存储了常用的函数、变量和数据结构等。这些库可以帮助开发者节省大量的时间和精力,避免重复编写相同的代码。当我们在 Linux 系统中开发程序时,经常会用到两种类型的程序库&am…

DeepSeek 系列模型:论文精读《A Survey of DeepSeek Models》

引言:一篇快速了解 DeepSeek 系列的论文。我在翻译时加入了一些可以提高 “可读性” 的连词 ✅ NLP 研 2 选手的学习笔记 笔者简介:Wang Linyong,NPU,2023级,计算机技术 研究方向:文本生成、大语言模型 论文…

机器人学习模拟框架 robosuite (3) 机器人控制代码示例

Robosuite框架是一个用于机器人模拟和控制的强大工具,支持多种类型的机器人。 官方文档:Overview — robosuite 1.5 documentation 开源地址:https://github.com/ARISE-Initiative/robosuite 目录 1、通过键盘或SpaceMouse远程控制机器人…

kakfa-3:ISR机制、HWLEO、生产者、消费者、核心参数负载均衡

1. kafka内核原理 1.1 ISR机制 光是依靠多副本机制能保证Kafka的高可用性,但是能保证数据不丢失吗?不行,因为如果leader宕机,但是leader的数据还没同步到follower上去,此时即使选举了follower作为新的leader&#xff…

【微知】如何查看Mellanox网卡上的光模块的信息?(ethtool -m enp1s0f0 看型号、厂商、生产日期等)

背景 服务器上插入的光模块经常被忽略,往往这里是定位问题最根本的地方。如何通过命令查看? 命令 ethtool提供了-m参数,m是module-info的意思,他是从光模块的eeprom中读取数据。(应该是用i2c协议读取的)…

yum源选要配置华为云的源,阿里云用不了的情况

curl -O /etc/yum.repos.d/CentOS-Base.repo https://repo.huaweicloud.com/repository/conf/CentOS-7-reg.repo

好数——前缀和思想(题目分享)

今天我的舍友去参加“传智杯”广东省的省赛,跟我说了这样一道题,他说他想不出来怎么去优化代码,怎么做都是套用两层for循环超时,下面我就根据题意,使用前缀和的算法去优化一下思路,题目本身是不难的&#x…

MWC 2025 | 移远通信大模型解决方案加速落地,引领服务机器人创新变革

随着人工智能、大模型等技术的蓬勃发展,生成式AI应用全面爆发。在此背景下,服务机器人作为大模型技术在端侧落地的关键场景,迎来了前所未有的发展机遇。 作为与用户直接交互的智能设备,服务机器人需要应对复杂场景下的感知、决策和…

springboot425-基于SpringBoot的BUG管理系统(源码+数据库+纯前后端分离+部署讲解等)

💕💕作者: 爱笑学姐 💕💕个人简介:十年Java,Python美女程序员一枚,精通计算机专业前后端各类框架。 💕💕各类成品Java毕设 。javaweb,ssm&#xf…

FineReport 操作注意

1.父单元格重复的时候,如何取消合并 效果如下: 只需要在单元格中,将数据设置为【列表】即可。 2.待定

3D手眼标定转换详细实施步骤及原理概述

3D手眼标定转换详细实施步骤及原理概述 一、手眼标定的核心目标二、3D手眼标定的原理概述一、基本概念与坐标系定义**二、数学建模与方程推导****1. 坐标变换的齐次矩阵表示****2. 手眼标定方程推导** **三、方程求解方法****1. 分离旋转与平移****2. 旋转矩阵求解****3. 平移向…

Verilog:SCCB控制器

目录 一、SCCB协议 (1)SCCB时序 (2)与I2C的区别 二、Verilog 实现 (1)设计要求 (2)设计要点 (3)模块完整代码 三、功能验证 (1)写…

与中国联通技术共建:通过obdiag分析OceanBase DDL中的报错场景

中国联通软件研究院(简称联通软研院)在全面评估与广泛调研后,在 2021年底决定采用OceanBase 作为基础,自研分布式数据库产品CUDB(即China Unicom Database,中国联通数据库)。目前,该…

大数据与网络安全讲座

🍅 点击文末小卡片 ,免费获取网络安全全套资料,资料在手,涨薪更快 大数据的价值为大家公认。业界通常以4个“V”来概括大数据的基本特征——Volume(数据体量巨大)、Variety(数据类型繁多)、Value(价值密度低)、Velocity(处理速度快…