「软件设计模式」装饰者模式(Decorator)
深入解析装饰者模式:动态扩展功能的艺术(C++实现)

一、模式思想与应用场景
1.1 模式定义
装饰者模式(Decorator Pattern)是一种结构型设计模式,它通过将对象放入包含行为的特殊封装对象中,动态地为原始对象添加新功能,比继承更灵活。
1.2 核心优势
- 动态添加功能:运行时修改对象行为
- 避免类爆炸:替代多层次的继承结构
- 遵循开闭原则:扩展开放,修改关闭
1.3 典型应用场景
- 需要动态/透明地给对象添加职责
- 需要撤销已添加的功能
- 不适合使用子类扩展的情况
- 常见应用:
- GUI组件装饰
- 流处理(如Java I/O)
- 游戏角色装备系统
- 咖啡订单系统(本文示例)
二、模式结构与C++实现
2.1 UML类图解析

2.2 咖啡订单系统实现
基础组件接口
#include <iostream>
#include <memory>
#include <string>
// 抽象组件
class Beverage {
public:virtual ~Beverage() = default;virtual std::string getDescription() const = 0;virtual double cost() const = 0;
};// 具体组件:浓缩咖啡
class Espresso : public Beverage {
public:std::string getDescription() const override {return "Espresso";}double cost() const override {return 1.99;}
};
装饰器基类
#include "component.h"
// 抽象装饰器
class CondimentDecorator : public Beverage {
public:explicit CondimentDecorator(std::unique_ptr<Beverage> beverage) : beverage_(std::move(beverage)) {}std::string getDescription() const override {return beverage_->getDescription();}double cost() const override {return beverage_->cost();}protected:std::unique_ptr<Beverage> beverage_;
};
具体装饰器实现
#include "decorator_abstract.h"// 牛奶装饰器
class Milk : public CondimentDecorator {
public:using CondimentDecorator::CondimentDecorator;std::string getDescription() const override {return beverage_->getDescription() + ", Milk";}double cost() const override {return beverage_->cost() + 0.45;}
};// 摩卡装饰器
class Mocha : public CondimentDecorator {
public:using CondimentDecorator::CondimentDecorator;std::string getDescription() const override {return beverage_->getDescription() + ", Mocha";}double cost() const override {return beverage_->cost() + 0.60;}
};
三、客户端使用示例
3.1 基础使用
#include <iostream>
#include <memory>
#include <string>
#include "decorator_entity.h"int main() {// 创建基础咖啡std::unique_ptr<Beverage> coffee = std::make_unique<Espresso>();// 第一次装饰:加牛奶coffee = std::make_unique<Milk>(std::move(coffee));// 第二次装饰:加摩卡coffee = std::make_unique<Mocha>(std::move(coffee));// 输出结果std::cout << "Order: " << coffee->getDescription() << "\n"<< "Total Cost: $" << coffee->cost() << std::endl;return 0;
}
3.2 输出结果
Order: Espresso, Milk, Mocha
Total Cost: $3.04
四、模式深度解析
4.1 设计亮点
- 动态组合:通过嵌套装饰实现功能叠加
- 透明性:装饰器与组件接口一致
- 弹性扩展:新装饰器不影响现有代码
- 智能指针管理:自动内存管理
4.2 与传统继承对比
| 特性 | 继承方案 | 装饰者模式 |
|---|---|---|
| 扩展方式 | 编译时静态扩展 | 运行时动态组合 |
| 类数量 | 指数级增长(组合爆炸) | 线性增长 |
| 功能叠加 | 固定组合 | 任意顺序组合 |
| 维护成本 | 修改基类影响所有子类 | 独立扩展互不影响 |
4.3 实现注意事项
- 接口一致性:装饰器必须完全实现组件接口
- 装饰顺序:不同装饰顺序可能影响最终结果
- 内存管理:
- 使用
unique_ptr自动管理生命周期 - 禁止拷贝操作(示例中已隐式实现)
- 使用
- 性能考量:多层嵌套可能影响性能
五、进阶实现技巧
5.1 装饰器撤销功能
// 移除装饰器类
template <typename T>
class RemovableDecorator : public CondimentDecorator {
public:using CondimentDecorator::CondimentDecorator;// 移除特定类型的装饰std::unique_ptr<Beverage> removeDecorator() {if (auto dec = dynamic_cast<T*>(beverage_.get())) {return std::move(dec->beverage_);}return std::move(beverage_);}std::string getDescription() const override {return beverage_->getDescription();}double cost() const override {return beverage_->cost();}
};
5.2 复合装饰器
// 复合装饰器 - 两倍装饰 将所选饮料配料翻倍
class DoubleDecorator : public CondimentDecorator {
public:using CondimentDecorator::CondimentDecorator;std::string getDescription() const override {return "'" + beverage_->getDescription() + "' x2";}double cost() const override {return beverage_->cost() * 2;}
};
5.3 调用方法
#include <iostream>
#include <memory>
#include <string>
#include "decorator_entity.h"int main() {// 创建基础咖啡std::unique_ptr<Beverage> coffee = std::make_unique<Espresso>();// 第一次装饰:加牛奶coffee = std::make_unique<Milk>(std::move(coffee));// 第二次装饰:加摩卡coffee = std::make_unique<Mocha>(std::move(coffee));// 输出结果std::cout << "Order: " << coffee->getDescription() << "\n"<< "Total Cost: $" << coffee->cost() << std::endl;// 使用示例auto rmver = std::make_unique<RemovableDecorator<Mocha>>(std::move(coffee));coffee = rmver->removeDecorator();// 输出结果std::cout << "Order: " << coffee->getDescription() << "\n"<< "Total Cost after remove Mocha: $" << coffee->cost() << std::endl;coffee = std::make_unique<DoubleDecorator>(std::move(coffee));std::cout << "Order: " << coffee->getDescription() << "\n"<< "Total Cost after double order: $" << coffee->cost() << std::endl;return 0;
}
5.4 执行结果
Order: Espresso, Milk, Mocha // 初始点单
Total Cost: $3.04
Order: Espresso, Milk // 移除Mocha
Total Cost after remove Mocha: $2.44
Order: 'Espresso, Milk' x2 // 复合装饰器,double order
Total Cost after double order: $4.88
六、模式应用扩展
6.1 实际工程案例
- 图形界面组件:
- 滚动条装饰文本框
- 边框装饰图片组件
- 网络通信:
- 加密装饰数据流
- 压缩装饰传输流
- 游戏开发:
- 装备系统增强角色属性
- 技能BUFF叠加效果
6.2 相关模式对比
| 模式 | 核心区别 |
|---|---|
| 适配器模式 | 改变对象接口 |
| 代理模式 | 控制访问,不添加功能 |
| 组合模式 | 统一处理对象集合 |
| 策略模式 | 整体替换算法 |
七、最佳实践指南
7.1 适用场景判断
✅ 推荐使用:
- 需要动态添加/撤销功能
- 使用继承会导致类爆炸
- 不同功能的排列组合场景
❌ 避免使用:
- 需要直接访问具体组件方法
- 装饰顺序影响业务逻辑
- 性能敏感的底层系统
7.2 实现原则
- 优先组合:使用对象组合而非继承
- 保持轻量:装饰器不应包含复杂逻辑
- 接口隔离:定义明确的组件接口
- 文档规范:明确装饰器的叠加规则
八、总结
装饰者模式通过灵活的对象组合代替僵化的类继承,为系统扩展提供了优雅的解决方案。在C++实现中:
- 使用
unique_ptr管理组件生命周期 - 通过抽象类保证接口一致性
- 利用现代C++特性简化实现
当面对需要动态扩展功能的场景时,装饰者模式就像编程世界的"俄罗斯套娃",让每个装饰器层层包裹核心组件,最终组合出强大的功能集合。
相关文章:
「软件设计模式」装饰者模式(Decorator)
深入解析装饰者模式:动态扩展功能的艺术(C实现) 一、模式思想与应用场景 1.1 模式定义 装饰者模式(Decorator Pattern)是一种结构型设计模式,它通过将对象放入包含行为的特殊封装对象中,动态地…...
CI/CD(二)docker-compose安装Jenkins
1、docker-compose.yml version: 3.8services:jenkins:image: jenkins/jenkins:lts # 使用官方的 Jenkins LTS 镜像container_name: jenkinsuser: root # 如果需要以 root 用户运行ports:- "8080:8080" # Jenkins Web 界面端口- "50000:50000" # 用于 Jen…...
OpenCV机器学习(1)人工神经网络 - 多层感知器类cv::ml::ANN_MLP
操作系统:ubuntu22.04 OpenCV版本:OpenCV4.9 IDE:Visual Studio Code 编程语言:C11 算法描述 cv::ml::ANN_MLP 是 OpenCV 库中的一部分,用于实现人工神经网络 - 多层感知器(Artificial Neural Network - Multi-Layer…...
ProxySQL构建PolarDB-X标准版高可用路由服务三节点集群
ProxySQL构建PolarDB-X标准版高可用路由服务三节点集群 一、PolarDB-X标准版主备集群搭建 三台机器上传 polardbx 包,包可以从官网https://openpolardb.com/download获取,这里提供离线rpm。 1、上传 polardbx 安装包 到 /opt目录下 rpm -ivh t-pol…...
15.1 Process(进程)类
版权声明:本文为博主原创文章,转载请在显著位置标明本文出处以及作者网名,未经作者允许不得用于商业目的。 通常开发时想要获得进程是比较困难的事,必须要调用CreateToolhelpSnapshot、ProcessFirst、ProcessNext等API或者诸如 Zw…...
elasticsearch8 linux版以服务的方式启动
1.创建系统服务文件 对于使用 systemd 作为系统初始化系统的 Linux 发行版(如 CentOS 7 及以上、Ubuntu 16.04 及以上),需要创建一个 systemd 服务文件。以 root 用户或具有 sudo 权限的用户身份执行以下操作: sudo vim /etc/sy…...
小米 R3G 路由器刷机教程(Pandavan)
小米 R3G 路由器刷机教程(Pandavan) 一、前言 小米 R3G 路由器以其高性价比和稳定的性能备受用户青睐。然而,原厂固件的功能相对有限,难以满足高级用户的个性化需求。刷机不仅可以解锁路由器的潜能,还能通过第三方固…...
某大型业务系统技术栈介绍【应对面试】
微服务架构【图】 微服务架构【概念】 微服务架构,是一种架构模式,它提倡将单一应用程序划分成一组小的服务,服务之间互相协调、互相配合,为用户提供最终价值。在微服务架构中,服务与服务之间通信时,通常是…...
【区块链】零知识证明基础概念详解
🌈个人主页: 鑫宝Code 🔥热门专栏: 闲话杂谈| 炫酷HTML | JavaScript基础 💫个人格言: "如无必要,勿增实体" 文章目录 零知识证明基础概念详解引言1. 零知识证明的定义与特性1.1 基本定义1.2 三个核心…...
建筑行业安全技能竞赛流程方案
一、比赛时间: 6月23日8:30分准时到场;9:00-10:00理论考试;10:10-12:00现场隐患答疑;12:00-13:30午餐;下午13:30-15:30现场…...
数据结构:图;邻接矩阵和邻接表
邻接矩阵: 1.概念: 邻接矩阵是图的存储结构之一,通过二维数组表示顶点间的连接关系。 2.具体例子 : 一.无向图邻接矩阵示例: 示例图(顶点:A、B、C,边:A-B、B-C&…...
DeepSeek-R1论文阅读及蒸馏模型部署
DeepSeek-R1论文阅读及蒸馏模型部署 文章目录 DeepSeek-R1论文阅读及蒸馏模型部署摘要Abstract一、DeepSeek-R1论文1. 论文摘要2. 引言3. DeepSeek-R1-Zero的方法3.1 强化学习算法3.2 奖励建模3.3 训练模版3.4 DeepSeek-R1-Zero的性能、自进化过程和顿悟时刻 4. DeepSeek-R1&am…...
OpenEuler学习笔记(三十三):在 OpenEuler 上搭建 OpenGauss 数据库环境
在 OpenEuler 上搭建 OpenGauss 数据库环境需要按照以下步骤进行。OpenGauss 是华为开源的一款高性能关系型数据库,支持高并发、高可用性和分布式部署。 1. 环境准备 确保你的 OpenEuler 系统满足以下要求: 操作系统:OpenEuler 20.03 LTS 或…...
[C++]多态详解
目录 一、多态的概念 二、静态的多态 三、动态的多态 3.1多态的定义 3.2虚函数 四、虚函数的重写(覆盖) 4.1虚函数 4.2三同 4.3两种特殊情况 (1)协变 (2)析构函数的重写 五、C11中的final和over…...
调用DeepSeek API接口:实现智能数据挖掘与分析
调用DeepSeek API接口:实现智能数据挖掘与分析 在当今数据驱动的时代,企业和开发者越来越依赖高效的数据挖掘与分析工具来获取有价值的洞察。DeepSeek作为一款先进的智能数据挖掘平台,提供了强大的API接口,帮助用户轻松集成其功能到自己的应用中。本文将详细介绍如何调用D…...
ffmpeg-cli-wrapper操作ffmpeg的工具
学习链接 ffmpeg-cli-wrapper - 内部封装了操作ffmpeg命令的java类库,它提供了一些类和方法,可以方便地构建和执行 ffmpeg 命令,而不需要直接操作字符串或进程。并且支持异步执行和进度监听 springboot-ffmpeg-m3u8-convertor - gitee代码 …...
【Qt】QObject类的主要功能
在 Qt 中,QObject 类是所有 Qt 对象的基类,提供了许多基础功能,使得 Qt 的对象系统能够有效地工作。它为其他类提供了核心的机制,比如信号和槽机制、对象树结构、内存管理等。 QObject 类的主要功能: 信号和槽机制&am…...
学习笔记之debian的thonny开发(尚未验证)--从stm32裸机到linux嵌入式系统
这应该算 stm32裸机用户 转 linux嵌入式系统 的入门学习笔记。 【鲁班猫】39-vnc远程桌面连接鲁班猫_哔哩哔哩_bilibili 本集的鲁班猫的视频介绍中,没有清晰明确指出需要linux开发板接入网络,接入网络可以使用有线网口或者wifi路由,有些提示…...
把 CSV 文件摄入到 Elasticsearch 中 - CSVES
在我们之前的很多文章里,我有讲到这个话题。在今天的文章中,我们就提重谈。我们使用一种新的方法来实现。这是一个基于 golang 的开源项目。项目的源码在 https://github.com/githubesson/csves/。由于这个原始的代码并不支持 basic security 及带有安全…...
PyQt组态软件 拖拽设计界面测试
PyQt组态软件测试 最近在研究PyQt,尝试写个拖拽设计界面的组态软件,目前实现的功能如下: 支持拖入控件,鼠标拖动控件位置 拖动控件边缘修改控件大小支持属性编辑器,修改当前选中控件的属性 拖动框选控件,点选控件 控…...
Chapter03-Authentication vulnerabilities
文章目录 1. 身份验证简介1.1 What is authentication1.2 difference between authentication and authorization1.3 身份验证机制失效的原因1.4 身份验证机制失效的影响 2. 基于登录功能的漏洞2.1 密码爆破2.2 用户名枚举2.3 有缺陷的暴力破解防护2.3.1 如果用户登录尝试失败次…...
谷歌浏览器插件
项目中有时候会用到插件 sync-cookie-extension1.0.0:开发环境同步测试 cookie 至 localhost,便于本地请求服务携带 cookie 参考地址:https://juejin.cn/post/7139354571712757767 里面有源码下载下来,加在到扩展即可使用FeHelp…...
synchronized 学习
学习源: https://www.bilibili.com/video/BV1aJ411V763?spm_id_from333.788.videopod.episodes&vd_source32e1c41a9370911ab06d12fbc36c4ebc 1.应用场景 不超卖,也要考虑性能问题(场景) 2.常见面试问题: sync出…...
Cloudflare 从 Nginx 到 Pingora:性能、效率与安全的全面升级
在互联网的快速发展中,高性能、高效率和高安全性的网络服务成为了各大互联网基础设施提供商的核心追求。Cloudflare 作为全球领先的互联网安全和基础设施公司,近期做出了一个重大技术决策:弃用长期使用的 Nginx,转而采用其内部开发…...
04-初识css
一、css样式引入 1.1.内部样式 <div style"width: 100px;"></div>1.2.外部样式 1.2.1.外部样式1 <style>.aa {width: 100px;} </style> <div class"aa"></div>1.2.2.外部样式2 <!-- rel内表面引入的是style样…...
Element Plus 表单(el-form)中关于正整数输入的校验规则
目录 1 单个正整数输入1.1 模板1.2 校验规则 2 两个正整数输入(联动)2.1 模板2.2 校验规则2.3 CSS 1 单个正整数输入 1.1 模板 <el-formref"formRef":model"formData":rules"formRules"label-width"150px"…...
dify打造数据可视化图表
一、概述 在日常工作和学习中,我们经常需要和数据打交道。无论是分析报告、项目展示,还是简单的数据洞察,一个清晰直观的图表,往往能胜过千言万语。 一款能让数据可视化变得超级简单的 MCP Server,由蚂蚁集团 AntV 团队…...
Linux C语言网络编程详细入门教程:如何一步步实现TCP服务端与客户端通信
文章目录 Linux C语言网络编程详细入门教程:如何一步步实现TCP服务端与客户端通信前言一、网络通信基础概念二、服务端与客户端的完整流程图解三、每一步的详细讲解和代码示例1. 创建Socket(服务端和客户端都要)2. 绑定本地地址和端口&#x…...
招商蛇口 | 执笔CID,启幕低密生活新境
作为中国城市生长的力量,招商蛇口以“美好生活承载者”为使命,深耕全球111座城市,以央企担当匠造时代理想人居。从深圳湾的开拓基因到西安高新CID的战略落子,招商蛇口始终与城市发展同频共振,以建筑诠释对土地与生活的…...
【Redis】笔记|第8节|大厂高并发缓存架构实战与优化
缓存架构 代码结构 代码详情 功能点: 多级缓存,先查本地缓存,再查Redis,最后才查数据库热点数据重建逻辑使用分布式锁,二次查询更新缓存采用读写锁提升性能采用Redis的发布订阅机制通知所有实例更新本地缓存适用读多…...
