当前位置: 首页 > news >正文

「软件设计模式」装饰者模式(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 设计亮点

  1. 动态组合:通过嵌套装饰实现功能叠加
  2. 透明性:装饰器与组件接口一致
  3. 弹性扩展:新装饰器不影响现有代码
  4. 智能指针管理:自动内存管理

4.2 与传统继承对比

特性继承方案装饰者模式
扩展方式编译时静态扩展运行时动态组合
类数量指数级增长(组合爆炸)线性增长
功能叠加固定组合任意顺序组合
维护成本修改基类影响所有子类独立扩展互不影响

4.3 实现注意事项

  1. 接口一致性:装饰器必须完全实现组件接口
  2. 装饰顺序:不同装饰顺序可能影响最终结果
  3. 内存管理
    • 使用unique_ptr自动管理生命周期
    • 禁止拷贝操作(示例中已隐式实现)
  4. 性能考量:多层嵌套可能影响性能

五、进阶实现技巧

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 实际工程案例

  1. 图形界面组件
    • 滚动条装饰文本框
    • 边框装饰图片组件
  2. 网络通信
    • 加密装饰数据流
    • 压缩装饰传输流
  3. 游戏开发
    • 装备系统增强角色属性
    • 技能BUFF叠加效果

6.2 相关模式对比

模式核心区别
适配器模式改变对象接口
代理模式控制访问,不添加功能
组合模式统一处理对象集合
策略模式整体替换算法

七、最佳实践指南

7.1 适用场景判断

✅ 推荐使用:

  • 需要动态添加/撤销功能
  • 使用继承会导致类爆炸
  • 不同功能的排列组合场景

❌ 避免使用:

  • 需要直接访问具体组件方法
  • 装饰顺序影响业务逻辑
  • 性能敏感的底层系统

7.2 实现原则

  1. 优先组合:使用对象组合而非继承
  2. 保持轻量:装饰器不应包含复杂逻辑
  3. 接口隔离:定义明确的组件接口
  4. 文档规范:明确装饰器的叠加规则

八、总结

装饰者模式通过灵活的对象组合代替僵化的类继承,为系统扩展提供了优雅的解决方案。在C++实现中:

  • 使用unique_ptr管理组件生命周期
  • 通过抽象类保证接口一致性
  • 利用现代C++特性简化实现

当面对需要动态扩展功能的场景时,装饰者模式就像编程世界的"俄罗斯套娃",让每个装饰器层层包裹核心组件,最终组合出强大的功能集合。

相关文章:

「软件设计模式」装饰者模式(Decorator)

深入解析装饰者模式&#xff1a;动态扩展功能的艺术&#xff08;C实现&#xff09; 一、模式思想与应用场景 1.1 模式定义 装饰者模式&#xff08;Decorator Pattern&#xff09;是一种结构型设计模式&#xff0c;它通过将对象放入包含行为的特殊封装对象中&#xff0c;动态地…...

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

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 cv::ml::ANN_MLP 是 OpenCV 库中的一部分&#xff0c;用于实现人工神经网络 - 多层感知器&#xff08;Artificial Neural Network - Multi-Layer…...

ProxySQL构建PolarDB-X标准版高可用路由服务三节点集群

ProxySQL构建PolarDB-X标准版高可用路由服务三节点集群 一、PolarDB-X标准版主备集群搭建 三台机器上传 polardbx 包&#xff0c;包可以从官网https://openpolardb.com/download获取&#xff0c;这里提供离线rpm。 1、上传 polardbx 安装包 到 /opt目录下 rpm -ivh t-pol…...

15.1 Process(进程)类

版权声明&#xff1a;本文为博主原创文章&#xff0c;转载请在显著位置标明本文出处以及作者网名&#xff0c;未经作者允许不得用于商业目的。 通常开发时想要获得进程是比较困难的事&#xff0c;必须要调用CreateToolhelpSnapshot、ProcessFirst、ProcessNext等API或者诸如 Zw…...

elasticsearch8 linux版以服务的方式启动

1.创建系统服务文件 对于使用 systemd 作为系统初始化系统的 Linux 发行版&#xff08;如 CentOS 7 及以上、Ubuntu 16.04 及以上&#xff09;&#xff0c;需要创建一个 systemd 服务文件。以 root 用户或具有 sudo 权限的用户身份执行以下操作&#xff1a; sudo vim /etc/sy…...

小米 R3G 路由器刷机教程(Pandavan)

小米 R3G 路由器刷机教程&#xff08;Pandavan&#xff09; 一、前言 小米 R3G 路由器以其高性价比和稳定的性能备受用户青睐。然而&#xff0c;原厂固件的功能相对有限&#xff0c;难以满足高级用户的个性化需求。刷机不仅可以解锁路由器的潜能&#xff0c;还能通过第三方固…...

某大型业务系统技术栈介绍【应对面试】

微服务架构【图】 微服务架构【概念】 微服务架构&#xff0c;是一种架构模式&#xff0c;它提倡将单一应用程序划分成一组小的服务&#xff0c;服务之间互相协调、互相配合&#xff0c;为用户提供最终价值。在微服务架构中&#xff0c;服务与服务之间通信时&#xff0c;通常是…...

【区块链】零知识证明基础概念详解

&#x1f308;个人主页: 鑫宝Code &#x1f525;热门专栏: 闲话杂谈&#xff5c; 炫酷HTML | JavaScript基础 ​&#x1f4ab;个人格言: "如无必要&#xff0c;勿增实体" 文章目录 零知识证明基础概念详解引言1. 零知识证明的定义与特性1.1 基本定义1.2 三个核心…...

建筑行业安全技能竞赛流程方案

一、比赛时间&#xff1a; 6月23日8&#xff1a;30分准时到场&#xff1b;9&#xff1a;00&#xff0d;10&#xff1a;00理论考试&#xff1b;10&#xff1a;10-12:00现场隐患答疑&#xff1b;12:00-13&#xff1a;30午餐&#xff1b;下午13&#xff1a;30-15&#xff1a;30现场…...

数据结构:图;邻接矩阵和邻接表

邻接矩阵&#xff1a; 1.概念&#xff1a; 邻接矩阵是图的存储结构之一&#xff0c;通过二维数组表示顶点间的连接关系。 2.具体例子 &#xff1a; 一.无向图邻接矩阵示例&#xff1a; 示例图&#xff08;顶点&#xff1a;A、B、C&#xff0c;边&#xff1a;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 是华为开源的一款高性能关系型数据库&#xff0c;支持高并发、高可用性和分布式部署。 1. 环境准备 确保你的 OpenEuler 系统满足以下要求&#xff1a; 操作系统&#xff1a;OpenEuler 20.03 LTS 或…...

[C++]多态详解

目录 一、多态的概念 二、静态的多态 三、动态的多态 3.1多态的定义 3.2虚函数 四、虚函数的重写&#xff08;覆盖&#xff09; 4.1虚函数 4.2三同 4.3两种特殊情况 &#xff08;1&#xff09;协变 &#xff08;2&#xff09;析构函数的重写 五、C11中的final和over…...

调用DeepSeek API接口:实现智能数据挖掘与分析

调用DeepSeek API接口:实现智能数据挖掘与分析 在当今数据驱动的时代,企业和开发者越来越依赖高效的数据挖掘与分析工具来获取有价值的洞察。DeepSeek作为一款先进的智能数据挖掘平台,提供了强大的API接口,帮助用户轻松集成其功能到自己的应用中。本文将详细介绍如何调用D…...

ffmpeg-cli-wrapper操作ffmpeg的工具

学习链接 ffmpeg-cli-wrapper - 内部封装了操作ffmpeg命令的java类库&#xff0c;它提供了一些类和方法&#xff0c;可以方便地构建和执行 ffmpeg 命令&#xff0c;而不需要直接操作字符串或进程。并且支持异步执行和进度监听 springboot-ffmpeg-m3u8-convertor - gitee代码 …...

【Qt】QObject类的主要功能

在 Qt 中&#xff0c;QObject 类是所有 Qt 对象的基类&#xff0c;提供了许多基础功能&#xff0c;使得 Qt 的对象系统能够有效地工作。它为其他类提供了核心的机制&#xff0c;比如信号和槽机制、对象树结构、内存管理等。 QObject 类的主要功能&#xff1a; 信号和槽机制&am…...

学习笔记之debian的thonny开发(尚未验证)--从stm32裸机到linux嵌入式系统

这应该算 stm32裸机用户 转 linux嵌入式系统 的入门学习笔记。 【鲁班猫】39-vnc远程桌面连接鲁班猫_哔哩哔哩_bilibili 本集的鲁班猫的视频介绍中&#xff0c;没有清晰明确指出需要linux开发板接入网络&#xff0c;接入网络可以使用有线网口或者wifi路由&#xff0c;有些提示…...

把 CSV 文件摄入到 Elasticsearch 中 - CSVES

在我们之前的很多文章里&#xff0c;我有讲到这个话题。在今天的文章中&#xff0c;我们就提重谈。我们使用一种新的方法来实现。这是一个基于 golang 的开源项目。项目的源码在 https://github.com/githubesson/csves/。由于这个原始的代码并不支持 basic security 及带有安全…...

PyQt组态软件 拖拽设计界面测试

PyQt组态软件测试 最近在研究PyQt,尝试写个拖拽设计界面的组态软件&#xff0c;目前实现的功能如下&#xff1a; 支持拖入控件&#xff0c;鼠标拖动控件位置 拖动控件边缘修改控件大小支持属性编辑器&#xff0c;修改当前选中控件的属性 拖动框选控件&#xff0c;点选控件 控…...

AISMM模型不是万能钥匙?3类不可替代的传统规则引擎场景+混合架构设计图(附2024年金融AI模型淘汰预警清单)

更多请点击&#xff1a; https://intelliparadigm.com 第一章&#xff1a;AISMM模型在金融行业中的应用 AISMM&#xff08;Adaptive Intelligent Sequential Modeling Mechanism&#xff09;是一种面向时序决策场景的动态建模框架&#xff0c;专为高噪声、低延迟、强监管的金融…...

Cortex-R82异常处理与调试机制深度解析

1. Cortex-R82异常处理架构解析在嵌入式实时系统中&#xff0c;异常处理机制直接决定了系统的可靠性和响应速度。Cortex-R82作为面向汽车电子和工业控制的高性能实时处理器&#xff0c;其异常处理架构设计体现了三个核心特征&#xff1a;确定性响应&#xff1a;所有异常入口和返…...

Cursor 频繁触发限流?通过自定义 API 满血解锁 Claude和GPT

Cursor 接入第三方 API 指南 前置条件与限制 Cursor 免费版无法使用自定义 Base URL 功能&#xff0c;必须订阅 Pro 或更高版本。 替代方案&#xff1a;Anthropic 官方工具 Claude Code 支持终端操作&#xff0c;无需编辑器订阅&#xff0c;兼容第三方中转接口。 获取 API 凭…...

AI编程助手成本优化实战:7项技能节省60% API开销

1. 项目概述&#xff1a;一份能帮你省下60% AI编程助手开销的实战手册 如果你正在用 Claude Code、Cursor 或者自己搭建的 AI 编程助手&#xff0c;并且开始为每月账单上的 API 调用费用感到肉疼&#xff0c;那咱们聊的就是一回事。我花了大半年时间&#xff0c;在管理超过20个…...

容器镜像同步工具comsu:轻量化私有仓库管理与DevOps实践

1. 项目概述&#xff1a;从“comsu”看容器镜像的轻量化实践最近在折腾容器化部署的时候&#xff0c;发现一个挺有意思的现象&#xff1a;很多开发者&#xff0c;包括我自己在内&#xff0c;都习惯性地去 Docker Hub 拉取那些“官方”或“热门”的镜像。比如跑个 Nginx&#xf…...

别再只问Wi-Fi几代了!手把手教你从802.11a到ax看懂路由器参数(附避坑指南)

从Wi-Fi 4到Wi-Fi 6&#xff1a;普通人也能看懂的选购实战手册 每次打开电商页面&#xff0c;看到"双频千兆"、"MU-MIMO"、"OFDMA"这些术语就头疼&#xff1f;别担心&#xff0c;今天我们就用最生活化的比喻&#xff0c;带你轻松掌握路由器的核心…...

3分钟搞定M3U8视频下载:告别命令行,拥抱图形化下载神器

3分钟搞定M3U8视频下载&#xff1a;告别命令行&#xff0c;拥抱图形化下载神器 【免费下载链接】N_m3u8DL-CLI-SimpleG N_m3u8DL-CLIs simple GUI 项目地址: https://gitcode.com/gh_mirrors/nm3/N_m3u8DL-CLI-SimpleG 还在为在线视频无法保存而烦恼吗&#xff1f;面对复…...

GUI Guider设计完UI后,如何一步步把代码‘喂’给STM32?保姆级移植与排错指南

GUI Guider设计完UI后&#xff0c;如何一步步把代码‘喂’给STM32&#xff1f;保姆级移植与排错指南 当你用GUI Guider完成精美的UI设计后&#xff0c;真正的挑战才刚刚开始。本文将带你穿越从导出代码到STM32实际运行的完整路径&#xff0c;解决那些官方文档没告诉你的"坑…...

初创团队在虚拟服务器上通过Taotoken低成本使用多模型能力

初创团队在虚拟服务器上通过Taotoken低成本使用多模型能力 1. 虚拟化环境中的AI能力集成挑战 初创团队在云虚拟机部署服务时&#xff0c;常面临AI能力接入的三大核心矛盾&#xff1a;模型选型灵活性需求与基础设施投入的矛盾、业务模块多样化与API管理复杂度的矛盾、研发阶段…...

多因子检测技术解锁动脉粥样硬化的分子密码:从生物标志物到系统评估

一、引言动脉粥样硬化是一种慢性、进行性的血管病变&#xff0c;其病理过程涉及脂质代谢紊乱、内皮功能障碍、炎症反应及氧化应激等多个环节。该疾病是心肌梗死、脑卒中等严重心血管事件的主要病理基础。早期识别与风险评估对于延缓疾病进展、改善临床预后具有重要意义。生物标…...