设计模式--装饰者模式(Decorator Pattern)
一、什么是装饰者模式(Decorator Pattern)
装饰者模式(Decorator Pattern)是一种结构型设计模式,它允许你在不修改现有对象的情况下,动态地将新功能附加到对象上。这种模式通过创建一个包装类,即装饰者,来包含原始对象,并在其上添加额外的行为或功能。这样,你可以在运行时选择不同的装饰者组合来实现不同的功能组合。
装饰者模式的关键思想是将功能细分为一系列小的组件,然后将这些组件通过装饰者按照需要进行组合。这种模式遵循开放-关闭原则,即对扩展开放,对修改关闭。
主要角色:
- 组件(Component): 定义了一个抽象接口,可以是具体组件和装饰者共同实现的接口,表示被装饰者的基本功能。
- 具体组件(Concrete Component): 实现了组件接口的具体对象,即被装饰的原始对象。
- 装饰者(Decorator): 保持一个指向组件对象的引用,并实现与组件接口相同的接口。它可以有多个具体装饰者的子类。
- 具体装饰者(Concrete Decorator): 扩展装饰者的功能,包装具体组件,并可能添加新的行为。
装饰者模式的优势包括:
- 可以动态地组合对象,实现不同的功能组合,避免了类爆炸问题(大量子类的产生)。
- 遵循开放-关闭原则,允许在不修改现有代码的情况下扩展功能。
然而,装饰者模式也可能引入大量的小类,增加了代码的复杂性。在使用装饰者模式时,需要谨慎选择需要装饰的组件,以及如何合理地组合装饰者,以确保代码的可读性和维护性。
二、装饰者模式的代码样例
当用C++实现装饰者模式时,我们可以通过创建基类(组件)和派生类(具体组件、装饰者、具体装饰者)来演示。以下是一个简单的示例:
#include <iostream>// 组件基类
class Coffee {
public:virtual double cost() = 0;virtual ~Coffee() {}
};// 具体组件
class Espresso : public Coffee {
public:double cost() override {return 1.99;}
};// 装饰者基类
class Decorator : public Coffee {
protected:Coffee* coffee;
public:Decorator(Coffee* coffee) : coffee(coffee) {}
};// 具体装饰者
class Milk : public Decorator {
public:Milk(Coffee* coffee) : Decorator(coffee) {}double cost() override {return coffee->cost() + 0.5;}
};class Sugar : public Decorator {
public:Sugar(Coffee* coffee) : Decorator(coffee) {}double cost() override {return coffee->cost() + 0.2;}
};int main() {Coffee* espresso = new Espresso();std::cout << "Cost of espresso: $" << espresso->cost() << std::endl;Coffee* milkEspresso = new Milk(espresso);std::cout << "Cost of milk espresso: $" << milkEspresso->cost() << std::endl;Coffee* milkSugarEspresso = new Sugar(milkEspresso);std::cout << "Cost of milk and sugar espresso: $" << milkSugarEspresso->cost() << std::endl;delete espresso;delete milkEspresso;delete milkSugarEspresso;return 0;
}
在这个示例中,我们定义了 Coffee 基类和一个具体组件 Espresso。然后,我们定义了 Decorator 基类,它包含了一个指向 Coffee 对象的引用,并有两个具体装饰者类 Milk 和 Sugar,它们分别在 Coffee 上添加了牛奶和糖的装饰。
在 main 函数中,我们创建了一个 Espresso 对象,然后通过装饰者模式依次创建了包含不同装饰的咖啡对象,并输出了其价格。
这个示例展示了如何使用C++实现装饰者模式,动态地为对象添加功能。
三、使用装饰者模式需要注意的问题
在使用装饰者模式时,需要注意以下几个问题:
- 类爆炸: 装饰者模式可能会引入大量的小类,每个装饰者都是一个单独的类。这可能会导致类的数量急剧增加,增加代码复杂性和维护成本。因此,在选择使用装饰者模式时,需要仔细权衡增加的类数量是否值得所提供的灵活性和扩展性。
- 装饰者顺序: 装饰者模式中,装饰者的顺序可能会影响最终的对象组合。你需要确保装饰者的顺序不会引起意外的行为,特别是在组合多个装饰者时。
- 代码可读性: 过度使用装饰者模式可能会使代码变得难以理解和维护。因为每个具体装饰者只负责添加一小部分功能,当功能需要嵌套多层装饰者时,代码可能会变得冗长且难以阅读。
- 接口一致性: 在创建装饰者时,需要确保它们与组件(基类)具有一致的接口。这样,装饰者才能无缝地替代组件,而不会引发类型不匹配的问题。
- 不适合所有情况: 装饰者模式适用于需要动态地添加功能的情况。如果功能不太可能改变,或者只有固定数量的组合方式,那么使用装饰者模式可能会过于复杂,不切实际。
- 继承和组合的选择: 在设计时,需要权衡是否使用继承或组合。装饰者模式使用了组合,但过多的组合也可能使系统变得复杂。在一些情况下,简单的继承可能更合适。
- 性能影响: 使用装饰者模式可能会在运行时引入一些额外的开销,因为每个装饰者都会对对象进行包装和处理。这可能会在需要高性能的场景下造成问题。
总之,在使用装饰者模式时,需要根据实际情况谨慎权衡,考虑其带来的灵活性和复杂性,确保模式的应用不会导致代码难以维护或性能下降。

相关文章:
设计模式--装饰者模式(Decorator Pattern)
一、什么是装饰者模式(Decorator Pattern) 装饰者模式(Decorator Pattern)是一种结构型设计模式,它允许你在不修改现有对象的情况下,动态地将新功能附加到对象上。这种模式通过创建一个包装类,…...
Spring三级缓存解决循环依赖
Spring三级缓存解决循环依赖 一 Spring bean对象的生命周期 二 三级缓存解决循环依赖 实现原理解析 spring利用singletonObjects, earlySingletonObjects, singletonFactories三级缓存去解决的,所说的缓存其实也就是三个Map 先实例化的bean会通过ObjectFactory半…...
Vscode自动移出不用的包
Vscode自动移出不用的包 在Vscode中删除不用的包、Vscode移出不用的包、Vscode移出不用的import包 设置 找到setting.json(在字体设置里面),添加如下配置 "editor.codeActionsOnSave": { "source.organizeImports": tru…...
leetcode做题笔记120. 三角形最小路径和
给定一个三角形 triangle ,找出自顶向下的最小路径和。 每一步只能移动到下一行中相邻的结点上。相邻的结点 在这里指的是 下标 与 上一层结点下标 相同或者等于 上一层结点下标 1 的两个结点。也就是说,如果正位于当前行的下标 i ,那么下一…...
weblogic/CVE-2018-2894文件上传漏洞复现
启动docker环境 查看帮助文档 环境启动后,访问http://your-ip:7001/console,即可看到后台登录页面。 执行docker-compose logs | grep password可查看管理员密码,管理员用户名为weblogic,密码为lFVAJ89F 登录后台页面,…...
windows10默认浏览器总是自动更改为Edge浏览器
在设置的默认应用设置中把默认浏览器改为chrome或其他之后他自动又会改回Edge。不得不说*软真的狗。 解决办法: 后来发现在Edge浏览器的设置中有这么一个选项,会很无耻的默认是Edge。把它关掉后重新设置就行了。...
系统架构设计师考试论文:论软件架构风格与应用
软件体系结构风格是描述某一特定应用领域中系统组织方式的惯用模式。体系结构风格定义一个系统家族,即一个体系结构定义一个词汇表和一纽约束。词汇表中包含一些构件和连接件类型,而这组约束指出系统是如何将这些构件和连接件组合起来的。体系结构风格反…...
xss-labs靶场通关详解
文章目录 前言level1level2level3level4level5level6level7level8level9level10level11level12level13level14level15level16level17level18level19&level20 前言 赶着假期结尾的时候,赶紧给自己找点任务做。现在对xss还是一知半解,只是了解个大概&a…...
关于类和接口
类和接口的区别,去除语法层面,谈谈编程层面的意义。 设计原则SOLID: S:单一职责(SRP),Single Responsibility Principle O:开-闭原则(OCP),Open-Closed Principle L:里氏替换(LSP)&…...
网络安全社区与资源分享: 推荐网络安全社区、论坛、博客、培训资源等,帮助从业者拓展人脉和知识。
第一章:引言 在当今数字化的世界中,网络安全问题变得愈发突出。随着各种新型威胁的涌现,网络安全从业者面临着持续不断的挑战。然而,正是因为这些挑战,网络安全社区应运而生,成为从业者们互相交流、学习和…...
SAP MM学习笔记26- SAP中 振替转记(转移过账)和 在库转送(库存转储)5 - 总结
SAP 中在库移动 不仅有入库(GR),出库(GI),也可以是单纯内部的转记或转送。 1,振替转记(转移过账) 具体查看我之前的文章。 SAP MM学习笔记26- SAP中 振替转记ÿ…...
Stable Diffusion WebUI提示词Prompts常用推荐
在Stable Diffusion(以下简称SD)中,提示词是很重要的一部分,写好提示词就能让画图事半功倍,下面介绍一款好用的工具,能很程度上让你更轻松。 他就是sd-webui-prompt-all-in-one 下面将详细介绍的安装以及使用,后面将详细讲解提示词(Prompt)应该如何写提示词才能使画…...
Android 13 Ethernet变更
Android13 有线变更 以太网相关的功能在Android12 和13 网络部分变化是不大的,Android11 到Android 12 网络部分无论是代码存放目录和代码逻辑都是有较多修改的,主要包括以下几个部分 限制了设置有线网参数设置接口方法 新增有线网开启关闭接口方法 新…...
基于单片机的超声波语音测距系统
一、系统方案 超声波具有指向性强,能量消耗缓慢,在介质中传播的距离较远,因而超声波经常用于距离的测量,如测距仪和物位测量仪等都可以通过超声波来实现。利用超声波检测往往比较迅速、方便、计算简单、易于做到实时控制ÿ…...
算法系列-力扣876-求链表的中间节点
# 求链表中间节点,如果有两个中间节点取后面那个 链表定义 // lc codestart /** * Definition for singly-linked list. * public class ListNode { * int val; * ListNode next; * ListNode() {} * ListNode(int val) { this.val val; } * …...
SpringBoot集成Redis、Redisson保姆教程【附源码】
1. SpringBoot集成Redis 关于Redis的安装,这里就不重复介绍了,需要的朋友可以看我之前的博文Redis多系统安装(Windows、Linux、Ubuntu) Redis原生命令大全,作者整理的很详细,大部分命令转化为java命令基本也是关键词 Redis 命令参考 接下来开始我们的正题,一起学习下…...
c++多线程中常用的使用方法
1)promise(保证)和future的联合使用,实现两个线程的数据传递 #include <iostream> #include<thread> #include<future>using namespace std;//promise用法:可以给线程一个值,而从另一个线程读出该值 // 实现了两个线程的数…...
【dart】dart基础学习使用(一):变量、操作符、注释和库操作
前言 学习dart语言。 注释 Dart 支持单行注释、多行注释和文档注释。 单行注释 单行注释以 // 开头。Dart 编译器将忽略从 // 到行尾之间的所有内容。 void main() {// 这是单行注释print(Welcome to my Llama farm!); }多行注释 多行注释以 /* 开始,以 / 结…...
element-plus 设置 el-date-picker 弹出框位置
前言 概述:el-date-picker 组件会自动根据空间范围进行选择比较好的弹出位置,但特定情况下,它自动计算出的弹出位置并不符合我们的实际需求,故需要我们手动设置。 存在的问题:element-plus 中 el-date-picker 文档中并…...
C++day7(auto关键字、lambda表达式、C++中的数据类型转换、C++标准模板库(STL)、list、文件操作)
一、Xmind整理: 关键词总结: 二、上课笔记整理: 1.auto关键字 #include <iostream>using namespace std;int fun(int a, int b, float *c, char d, double *e,int f) {return 12; }int main() {//定义一个函数指针,指向fu…...
嵌入式开发者的串口工具选择题:minicom vs microcom,谁更适合你的Linux板卡?
嵌入式Linux串口工具深度对比:minicom与microcom的实战选型指南 在嵌入式Linux开发中,串口调试如同工程师的"听诊器",是连接开发者与硬件设备的重要桥梁。面对资源受限的嵌入式环境,选择一款合适的串口工具往往能事半功…...
5秒完成B站缓存视频转换:m4s到MP4无损转换完整指南
5秒完成B站缓存视频转换:m4s到MP4无损转换完整指南 【免费下载链接】m4s-converter 一个跨平台小工具,将bilibili缓存的m4s格式音视频文件合并成mp4 项目地址: https://gitcode.com/gh_mirrors/m4/m4s-converter 你是否曾为B站缓存视频无法在其他…...
中控SCADA的VBS脚本玩不转了?试试用Python来“降维打击”,搞定复杂数据处理与模型调用
中控SCADA的VBS脚本玩不转了?试试用Python来“降维打击”,搞定复杂数据处理与模型调用 在工业自动化领域,中控SCADA系统长期扮演着数据采集与监控的核心角色。然而,当项目需求从简单的数据记录升级到需要复杂分析、预测性维护或实…...
STR71X芯片JTAG失效分析与Bootloader恢复指南
1. STR71X设备JTAG接口失效的典型场景分析当使用Keil MDK开发环境和ULINK2调试器连接STR71X系列芯片时,开发者常会遇到"Couldnt stop ARM device"的错误提示。这种情况通常发生在两种典型场景:芯片意外进入了低功耗模式(Power-down…...
量子门合成技术GULPS:异构硬件下的高效量子电路编译
1. 量子门合成基础与GULPS创新点 量子计算的核心操作单元是量子门,其中双量子位门(如CNOT、iSWAP等)在构建量子算法中扮演着关键角色。传统量子门合成方法主要围绕CNOT门展开,这种单一视角在面对现代量子硬件日益丰富的异构指令集…...
财经类大学生考什么证书?2026年最新考证指南与含金量解析
每到开学季或者寒暑假,总有不少财经专业的同学私下问我:“现在的就业环境这么卷,我是不是该把能考的证都考了?” 看着大家手里厚厚的备考资料和焦虑的眼神,我特别能理解这种心情。毕竟在财经这个圈子里,证书…...
VSCode + Modelsim 搭建Verilog开发环境:除了语法检查,还能这样玩?
VSCode与ModelSim深度集成:打造高效Verilog开发工作流 在数字电路设计领域,Verilog作为硬件描述语言的标准之一,其开发效率直接影响项目进度。传统开发模式中,工程师需要在多个工具间频繁切换——编辑器用于编码,Model…...
从NTC103到PT100:手把手教你为Arduino和STM32选型与编程(温度传感器实战)
从NTC103到PT100:手把手教你为Arduino和STM32选型与编程(温度传感器实战) 在物联网和智能硬件项目中,温度监测是最基础也最关键的环节之一。无论是恒温箱、环境监测站还是工业控制系统,选择一款合适的温度传感器往往决…...
STM32 SPI驱动W25Q128避坑指南:CubeMX配置、时序模式与读写超时那些事儿
STM32 SPI驱动W25Q128实战避坑指南:从时序陷阱到性能调优 1. 当SPI遇上Flash:硬件工程师的暗礁地带 在嵌入式存储解决方案中,W25Q128系列SPI Flash凭借其紧凑封装和简单接口,已成为众多STM32项目的标配外设。但看似简单的四线接口…...
LabVIEW项目实战:用‘类+队列’模式管理仪器参数,告别全局变量混乱
LabVIEW工程实践:基于类与队列的仪器参数管理框架设计 在工业自动化测试系统中,仪器参数管理一直是困扰工程师的典型难题。当系统需要同时控制网口、串口、GPIB等多种接口的测试设备时,传统的全局变量方案会导致参数耦合、修改不同步等问题。…...
