设计模式 15 Decorator Pattern 装饰器模式
设计模式 15 Decorator Pattern 装饰器模式
1.定义
Decorator Pattern 装饰器模式是一种结构型设计模式,它允许在运行时给对象添加新的行为或职责,而无需修改对象的源代码。这种模式通过创建一个包装对象,也称为装饰器,来包裹原始对象,装饰器对象与原始对象有相同的接口,因此可以在不改变客户端代码的情况下,增加或修改对象的功能。
装饰器模式的优点包括:
动态地给对象添加新的行为,而无需修改对象的源代码或继承结构。
可以独立地增加对象的功能,因为每个装饰器都是独立的类。
保持了类的单一职责,使得代码更易于维护和扩展。
装饰器模式通常用于添加非核心功能,如日志、性能追踪、缓存等,而不会影响对象的核心行为。
2.内涵
Decorator Pattern 的主要组成部分:
- Component(组件):这是定义对象接口的抽象类或接口。所有可以装饰的对象都必须实现这个接口,这样装饰器才能与它们互换。
- Concrete Component(具体组件):这是 Component 接口的实现,是将要被装饰的对象。它定义了实际的行为和职责。
- Decorator(装饰器):这是 Component 接口的实现,它持有一个 Component 对象的引用。装饰器可以是抽象的,也可以包含具体的行为。装饰器对象可以添加新的行为或修改 Component 对象的行为。
- Concrete Decorator(具体装饰器):这是 Decorator 的具体实现,它给 Component 添加新的行为或职责。每个 Concrete Decorator 都可以添加不同的功能,也可以堆叠多个 Decorator 来增强对象的功能。
组件之间的调用图如下图所示:
+------------+| Component |+------------+|| 继承/实现V+--------------+| Decorator |+--------------+|| 继承/实现V+-------------------+| Concrete Decorator|+-------------------+|| 持有V+-------------------+| Concrete Component|+-------------------+
每个模块的作用如下:
- Component Interface:定义了公共接口,使得装饰器和组件可以互相协作。
- Concrete Component:实现了 Component 接口,定义了具体的行为和状态,是被装饰的对象。
- Decorator:作为抽象装饰器,持有 Component 的引用,实现 Component 接口,以保持与组件的兼容性。
- Concrete Decorator:具体实现了装饰器的逻辑,添加或修改了 Concrete Component 的行为。可以有多个 Concrete Decorator,每个实现不同的增强功能。
3.使用示例
#include <iostream>
#include <string>using namespace std;// Component interface - defines the basic ice cream
// operations.
class IceCream {
public:virtual string getDescription() const = 0;virtual double cost() const = 0;
};// Concrete Component - the basic ice cream class.
class VanillaIceCream : public IceCream {
public:string getDescription() const override{return "Vanilla Ice Cream";}double cost() const override { return 160.0; }
};// Decorator - abstract class that extends IceCream.
class IceCreamDecorator : public IceCream {
protected:IceCream* iceCream;public:IceCreamDecorator(IceCream* ic): iceCream(ic){}string getDescription() const override{return iceCream->getDescription();}double cost() const override{return iceCream->cost();}
};// Concrete Decorator - adds chocolate topping.
class ChocolateDecorator : public IceCreamDecorator {
public:ChocolateDecorator(IceCream* ic): IceCreamDecorator(ic){}string getDescription() const override{return iceCream->getDescription()+ " with Chocolate";}double cost() const override{return iceCream->cost() + 100.0;}
};// Concrete Decorator - adds caramel topping.
class CaramelDecorator : public IceCreamDecorator {
public:CaramelDecorator(IceCream* ic): IceCreamDecorator(ic){}string getDescription() const override{return iceCream->getDescription() + " with Caramel";}double cost() const override{return iceCream->cost() + 150.0;}
};// 测试案例分析调用
int main()
{// Create a vanilla ice creamIceCream* vanillaIceCream = new VanillaIceCream();cout << "Order: " << vanillaIceCream->getDescription()<< ", Cost: Rs." << vanillaIceCream->cost()<< endl;// Wrap it with ChocolateDecoratorIceCream* chocolateIceCream= new ChocolateDecorator(vanillaIceCream);cout << "Order: " << chocolateIceCream->getDescription()<< ", Cost: Rs." << chocolateIceCream->cost()<< endl;// Wrap it with CaramelDecoratorIceCream* caramelIceCream= new CaramelDecorator(chocolateIceCream);cout << "Order: " << caramelIceCream->getDescription()<< ", Cost: Rs." << caramelIceCream->cost()<< endl;delete vanillaIceCream;delete chocolateIceCream;delete caramelIceCream;return 0;
}
4.注意事项
在使用 Decorator Pattern 时,需要注意以下几点:
- 性能影响:装饰器可能会增加对象的创建和管理成本,特别是在需要大量创建和销毁对象的场景下。因此,需要权衡装饰器带来的灵活性和可能的性能损失。
- 代码复杂性:如果过度使用装饰器,可能会导致代码结构变得复杂,难以理解和维护。确保每个装饰器都有明确的职责,并保持代码的简洁性。
- 类型检查和强类型语言:在强类型语言中,装饰器可能会隐藏原始对象的类型,这可能导致类型检查问题。使用类型注解或接口可以帮助解决这个问题。
- 一致性:确保所有装饰器的行为与组件接口保持一致,否则可能会导致客户端代码出错或行为不一致。
- 可组合性:虽然装饰器可以堆叠,但过多的装饰器可能导致代码难以理解和调试。考虑使用组合模式来组合多个功能,而不是一次性添加多个装饰器。
- 状态管理:如果组件的状态对行为有影响,确保装饰器正确处理和传递这些状态,以避免意外的行为。
- 设计时的考虑:在设计系统时,提前考虑是否需要使用装饰器,因为它可能影响到类的设计和接口的定义。在开始编码之前,充分理解需求和扩展性要求,以便做出最佳决策。
5.最佳实践
该模式,最佳实践包括以下这些点:
- 保持装饰器和组件接口一致:装饰器应该与组件有相同的接口,这样客户端代码可以透明地使用装饰后的对象,而无需知道它是装饰器还是原始组件。
- 避免深度装饰:虽然可以堆叠多个装饰器,但过多的装饰可能导致代码复杂性增加。如果需要添加大量功能,可能需要考虑其他设计模式,如组合模式或使用类的继承。
- 使用接口而非具体类:装饰器模式通常与接口一起使用,因为接口允许更灵活的替换和扩展。如果使用具体类,可能会限制装饰器的通用性。
- 明确职责:每个装饰器应专注于添加或修改特定的行为,而不是试图一次性处理所有额外功能。这样可以保持代码的清晰和可维护性。
- 使用装饰器来扩展功能:装饰器模式最适合用于添加非核心功能,如日志、缓存、权限控制等,这些功能可以独立于核心业务逻辑存在。
- 避免与继承混淆:装饰器模式是作为继承的替代方案,特别是当需要动态地添加或移除行为时。如果新的行为是静态的,并且适用于所有对象,那么继承可能更合适。
- 测试和文档:确保为装饰器编写测试用例,并在文档中明确说明装饰器的作用,以便其他开发者理解其功能和使用方式。
6.总结
该模式在使用时可能存在以下“坑”:类型混淆:装饰器可能会隐藏原始对象的类型,导致类型检查问题。例如,在强类型语言中,如果装饰器没有正确地保持原始类型信息,可能会在编译时或运行时遇到错误。例如,Java 中的 InputStream 和其装饰器,如果不注意类型转换,可能会导致类型安全问题。此外,性能开销也是需要考虑的地方。
相关文章:
设计模式 15 Decorator Pattern 装饰器模式
设计模式 15 Decorator Pattern 装饰器模式 1.定义 Decorator Pattern 装饰器模式是一种结构型设计模式,它允许在运行时给对象添加新的行为或职责,而无需修改对象的源代码。这种模式通过创建一个包装对象,也称为装饰器,来包裹原…...
cuda11.8安装torch2.0.1
pip install torch2.0.1 torchvision0.15.2 torchaudio2.0.2 --index-url https://download.pytorch.org/whl/cu118...
新手困 ViewModel与Activting的databinding2个对象 区别
在Android开发中,ViewModel与Activity的Binding并不是同一个概念,它们分别指的是不同的功能和用途。 ViewModel: ViewModel是一个为UI界面提供数据的类,它负责管理Activity或Fragment的数据。ViewModel类持有数据的引用,即使配置…...
Cocos Creator 声音播放与管理详解
Cocos Creator 是一款非常流行的游戏开发引擎,它提供了丰富的功能和工具,让开发者可以轻松构建出高质量的游戏。在游戏开发中,声音是一个非常重要的元素,可以为游戏增添氛围和趣味性。在本文中,我们将详细介绍Cocos Cr…...

今日早报 每日精选15条新闻简报 每天一分钟 知晓天下事 5月26日,星期日
每天一分钟,知晓天下事! 2024年5月26日 星期日 农历四月十九 1、 医保局:支持将符合条件的村卫生室纳入医保定点,方便农村居民就医。 2、 网传养老金储备严重不足?央视辟谣:这笔钱二十多年来从未动用过&a…...

IDEA快速生成类注释和方法注释的方法
1.生成类、接口、枚举、注解等文件的注释,不仅仅是class 2.生成方法注释的 可在方法上方空行输入/** 按enter键快速生成。生成的样式如下: PS:生成的返回值带一堆英文文字说明,感觉没必要 如果想生成比较全面的方法注释,如作者&…...
[集群聊天服务器]----(七)业务模块之一对一聊天、添加好友函数、好友类以及离线消息类
接着[集群聊天服务器]----(六)业务模块之用户注册、登录、退出以及客户端异常退出函数中对于业务模块的用户注册、登录、退出以及客户端异常退出函数的剖析,现在我们对点对点聊天以及添加好友的实现进行剖析。 点对点聊天 当客户端输入msgidONE_CHAT_MSG时&#x…...
java中使用jedis连接redis
4.java中使用jedis连接redis...

【多线程开发 2】从代码到实战TransmittableThreadLocal
【多线程开发 2】从代码到实战TransmittableThreadLocal 本文将从以下几个点讲解TransmittableThreadLocal(为了方便写以下简称ttl): 前身 是什么? 可以用来做什么? 源码原理 实战 前身 ThreadLocal 要了解ttl就要先了解Java自带的类…...
【车载以太网测试从入门到精通】——SOME/IP协议测试
系列文章目录 【车载以太网测试从入门到精通】系列文章目录汇总 文章目录 系列文章目录前言一、SOME/IP时间参数1.INITIAL_DELAY时间2.REPETITIONS_MAX次数3.REPETITIONS_BASE_DELAY时间4.CYCLIC_OFFER_DELAY时间5.TIME_TO_LIVE时间6.SUBSCRIBE_RETRY_DELAY时间二、SOME/IP服务…...
作业39 sqrt应用
目录 判断完全平方数 题目描述 输出所有因数 题目描述 因子求和 题目描述 判断素数 题目描述 判断完全平方数 题目描述 输入一个整数,判断他是否是完全平方数,如果是,输出yes,否则输出no 样例 样例…...
springboot 实现跨域的几种方式
1、跨域的原因: 由于同源策略(Same Origin Policy)的限制,浏览器不允许跨域请求。同源策略规定,A网页设置的Cookie、LocalStorage和IndexDB无法被同源以外的网页读取。 2、原因: 1)浏览器的同源策略(Same Origin Policy)限制了跨域请求。主要…...

springmvc Web上下文初始化
Web上下文初始化 web上下文与SerlvetContext的生命周期应该是相同的,springmvc中的web上下文初始化是由ContextLoaderListener来启动的 web上下文初始化流程 在web.xml中配置ContextLoaderListener <listener> <listener-class>org.springframework.…...
Verilog实战学习到RiscV - 2 : wire 和 reg 的区别
Verilog: wire 和 reg 的区别 1 引言 看Verilog例子过程中,总是分不清 wire 和 reg 的区别。这篇文章把两者放在一起总结一下,并且对比何时使用它们。 1.1 wire :组合逻辑 wire 是 Verilog 设计中的简单导线(或任意宽度的总线…...
OpenGL给定直线起点和终点不同的颜色,使用中点Bresenham画线
用鼠标左键按下确定直线起点,鼠标左键抬起确定直线终点。放一部分代码。 // 中点Bresenham算法.cpp : 定义控制台应用程序的入口点。 //#include "stdafx.h" #include <GL/glut.h> #include <iostream> #include <cmath>int windowWidt…...
IT行业的现状与未来发展趋势:从云计算到量子计算的技术变革
随着技术的不断进步,IT行业已经成为推动全球经济和社会发展的关键力量。从云计算、大数据、人工智能到物联网、5G通信和区块链,这些技术正在重塑我们的生活和工作方式。本文将深入探讨当前IT行业的现状,并展望未来发展趋势,旨在为…...

电脑远程控制另一台电脑怎么弄?
可以远程控制另一台电脑吗? “你好,我对远程访问技术不太了解。现在,我希望我的朋友可以远程控制我的Windows 10电脑,以便她能帮我解决一些问题。请问,有没有免费的方法可以实现这种远程控制?我该如何操作…...

软件设计师备考 | 案例专题之面向对象设计 概念与例题
相关概念 关系 依赖:一个事物的语义依赖于另一个事物的语义的变化而变化 关联:一种结构关系,描述了一组链,链是对象之间的连接。分为组合和聚合,都是部分和整体的关系,其中组合事物之间关系更强。两个类之…...

UniApp 2.0可视化开发工具:引领前端开发新纪元
一、引言 在移动互联网迅猛发展的今天,移动应用开发已经成为前端开发的重要方向之一。为了简化移动应用开发流程,提高开发效率,各大开发平台不断推出新的工具和框架。UniApp作为一款跨平台的移动应用开发框架,自诞生以来就备受开…...
前端调用浏览器录音功能且生成文件(vue)
如果可以实现记得点赞分享,谢谢老铁~ 首先在页面中给两个按钮,分别是“开始录音”,“结束录音”。以及录音成功后生成一个下载语音的链接。 1. 先看页面展示 <template><div><button click"startRecording…...

IDEA运行Tomcat出现乱码问题解决汇总
最近正值期末周,有很多同学在写期末Java web作业时,运行tomcat出现乱码问题,经过多次解决与研究,我做了如下整理: 原因: IDEA本身编码与tomcat的编码与Windows编码不同导致,Windows 系统控制台…...

XCTF-web-easyupload
试了试php,php7,pht,phtml等,都没有用 尝试.user.ini 抓包修改将.user.ini修改为jpg图片 在上传一个123.jpg 用蚁剑连接,得到flag...

Lombok 的 @Data 注解失效,未生成 getter/setter 方法引发的HTTP 406 错误
HTTP 状态码 406 (Not Acceptable) 和 500 (Internal Server Error) 是两类完全不同的错误,它们的含义、原因和解决方法都有显著区别。以下是详细对比: 1. HTTP 406 (Not Acceptable) 含义: 客户端请求的内容类型与服务器支持的内容类型不匹…...
rknn优化教程(二)
文章目录 1. 前述2. 三方库的封装2.1 xrepo中的库2.2 xrepo之外的库2.2.1 opencv2.2.2 rknnrt2.2.3 spdlog 3. rknn_engine库 1. 前述 OK,开始写第二篇的内容了。这篇博客主要能写一下: 如何给一些三方库按照xmake方式进行封装,供调用如何按…...
Cesium1.95中高性能加载1500个点
一、基本方式: 图标使用.png比.svg性能要好 <template><div id"cesiumContainer"></div><div class"toolbar"><button id"resetButton">重新生成点</button><span id"countDisplay&qu…...

为什么需要建设工程项目管理?工程项目管理有哪些亮点功能?
在建筑行业,项目管理的重要性不言而喻。随着工程规模的扩大、技术复杂度的提升,传统的管理模式已经难以满足现代工程的需求。过去,许多企业依赖手工记录、口头沟通和分散的信息管理,导致效率低下、成本失控、风险频发。例如&#…...
1688商品列表API与其他数据源的对接思路
将1688商品列表API与其他数据源对接时,需结合业务场景设计数据流转链路,重点关注数据格式兼容性、接口调用频率控制及数据一致性维护。以下是具体对接思路及关键技术点: 一、核心对接场景与目标 商品数据同步 场景:将1688商品信息…...
Robots.txt 文件
什么是robots.txt? robots.txt 是一个位于网站根目录下的文本文件(如:https://example.com/robots.txt),它用于指导网络爬虫(如搜索引擎的蜘蛛程序)如何抓取该网站的内容。这个文件遵循 Robots…...
python报错No module named ‘tensorflow.keras‘
是由于不同版本的tensorflow下的keras所在的路径不同,结合所安装的tensorflow的目录结构修改from语句即可。 原语句: from tensorflow.keras.layers import Conv1D, MaxPooling1D, LSTM, Dense 修改后: from tensorflow.python.keras.lay…...
【生成模型】视频生成论文调研
工作清单 上游应用方向:控制、速度、时长、高动态、多主体驱动 类型工作基础模型WAN / WAN-VACE / HunyuanVideo控制条件轨迹控制ATI~镜头控制ReCamMaster~多主体驱动Phantom~音频驱动Let Them Talk: Audio-Driven Multi-Person Conversational Video Generation速…...