设计模式之装饰器模式:让对象功能扩展更优雅的艺术

一、什么是装饰器模式
装饰器模式(Decorator Pattern)是一种结构型设计模式(Structural Pattern),它允许用户通过一种灵活的方式来动态地给一个对象添加一些额外的职责。就增加功能来说,装饰器模式相比使用子类更为灵活。这种模式创建了一个包装对象,也就是装饰器,来包裹真实对象。
二、装饰器模式的原理
装饰器模式的工作原理是通过创建一个包装对象,也就是装饰器,来包裹真实对象。装饰器通常会持有一个被装饰对象的引用,并在执行某些操作时,将这个请求委托给被装饰的对象,同时还可以在委托之前或之后添加一些附加操作。这样,我们就可以在不修改原有类结构的情况下,为对象添加新的功能或改变其行为。
三、装饰器模式的结构
装饰器模式主要包含以下几个角色:
组件(Component):定义一个接口,装饰器和被装饰者共同需要实现的接口,且装饰器可以给其实现类动态地添加一些职责。
具体组件(Concrete Component):Component的实现类,定义了一个具体的对象,扮演被装饰的角色。
装饰器(Decorator):同样是Component的实现类,同时持有一个Component对象的引用,通过调用该引用的方法来实现相应接口。
具体装饰器(Concrete Decorator):Decorator的子类,负责向Component角色添加新的功能。
四、装饰器模式的应用场景
装饰器模式非常适用于以下场景:
给对象添加额外的职责:当对象需要频繁地增加功能时,装饰器模式提供了一种灵活的方式来添加或移除这些功能,而无需修改对象的原有结构。
需要保持对象的接口不变:如果修改对象的接口会影响到许多其他的客户端代码,那么使用装饰器模式可以在不改变接口的情况下,为对象添加新的功能。
通过组合而非继承来扩展对象的功能:装饰器模式允许我们通过组合多个装饰器来扩展对象的功能,而不是通过创建大量的子类。这样可以避免类爆炸的问题,使系统更加灵活和可扩展。
运行时动态地添加或移除功能:在运行时,我们可以根据需要动态地添加或移除装饰器,从而改变对象的行为。这种灵活性是继承关系所不具备的。
五、装饰器模式的优缺点
5.1. 优点
可以在运行时动态地添加或移除功能,而无需修改现有的代码,更加灵活。
提供一种动态的方式来扩展一个对象的功能,在运行时选择不同的具体装饰器,从而实现不同的行为。
遵循开闭原则,对扩展开放,对修改关闭。这意味着我们可以在不修改现有代码的情况下,通过添加新的装饰器类来扩展系统的功能。
可以对一个对象进行多次装饰,通过使用不同的具体装饰类以及这些装饰类的排列组合可以创造出很多不同行为的组合,得到更加强大的对象.
5.2. 缺点
随着装饰器数量的增加,类的数量也会增加,这可能会使系统变得更加复杂和难以理解,且大量的对象还会占用系统更多资源,在一定程度上影响系统的性能。
由于装饰器模式本质上是一种递归调用,使得出现问题时排查错误较困难,需要逐级排查,较为繁琐。
六、装饰器模式示例
假设我们有一个咖啡店,提供不同类型的咖啡和可以添加的配料(如牛奶、糖等),我们可以使用装饰器模式来实现这个需求。
6.1. 定义组件接口
首先,我们定义一个咖啡的接口,它包含一个getDescription方法和一个cost方法来分别描述咖啡和计算价格:
public interface Beverage {String getDescription();double cost();
}
6.2. 创建具体组件
然后,我们创建一个实现了Beverage接口的具体咖啡类,比如Espresso。
public class Espresso implements Beverage{@Overridepublic String getDescription() {return "Espresso";}
@Overridepublic double cost() {return 1.99;}
}
6.3. 创建装饰器角色
接下来,我们定义一个装饰器类,它实现了Beverage接口并持有一个Beverage对象的引用。
public abstract class CondimentDecorator implements Beverage{protected Beverage beverage;
public CondimentDecorator(Beverage beverage) {this.beverage = beverage;}
@Overridepublic String getDescription() {return beverage.getDescription();}
@Overridepublic double cost() {return beverage.cost();}
}
6.4. 创建具体装饰角色
最后,我们创建具体的装饰类,比如Milk和Sugar,它们都继承自CondimentDecorator并添加自己特有的功能(如描述和价格)。
public class Milk extends CondimentDecorator{public Milk(Beverage beverage) {super(beverage);}
@Overridepublic String getDescription() {return beverage.getDescription() + ", Milk";}@Overridepublic double cost() {return .10 + beverage.cost();}
}
public class Sugar extends CondimentDecorator{public Sugar(Beverage beverage) {super(beverage);}
@Overridepublic String getDescription() {return super.getDescription() + ", Sugar";}@Overridepublic double cost() {return .10 + super.cost();}
}
6.5. 创建客户端测试制作咖啡
最后,我们通过一个客户端类来展示如何使用这些装饰器来构建和展示不同的咖啡组合。
public class MakeCoffee {public static void main(String[] args) {Beverage beverage = new Espresso();System.out.println(beverage.getDescription() + " $" + beverage.cost());
Beverage beverage2 = new Milk(new Espresso());System.out.println(beverage2.getDescription() + " $" + beverage2.cost());
Beverage beverage3 = new Milk(new Sugar(new Espresso()));System.out.println(beverage3.getDescription() + " $" + beverage3.cost());
// 可以继续添加更多的装饰器,如摩卡、香草等}
}
6.6. 测试结果
以下为示例运行结果:

在上面的例子中,我们首先创建了一个Espresso对象,并直接打印了它的描述和价格。然后,我们创建了一个加牛奶的Espresso,即Milk(new Espresso()),并打印了它的描述和价格。最后,我们创建了一个既加牛奶又加糖的Espresso,即Milk(new Sugar(new Espresso())),并打印了它的描述和价格。这样,我们就可以看到装饰器模式如何动态地给对象添加额外的职责。
七、总结
装饰器模式是一种强大的设计模式,它提供了一种灵活的方式来动态地给对象添加额外的职责。通过组合而非继承的方式,装饰器模式能够在不改变对象接口的情况下,为对象添加新的功能,从而提高了系统的灵活性和可扩展性。然而,我们也需要注意到装饰器模式的缺点,并在设计系统时权衡其利弊,以确保系统既灵活又易于理解和维护。
在实际应用中,我们应该根据具体的需求和场景来选择合适的设计模式。对于需要动态扩展功能且保持接口不变的场景,装饰器模式无疑是一个值得考虑的选择。
相关文章:
设计模式之装饰器模式:让对象功能扩展更优雅的艺术
一、什么是装饰器模式 装饰器模式(Decorator Pattern)是一种结构型设计模式(Structural Pattern),它允许用户通过一种灵活的方式来动态地给一个对象添加一些额外的职责。就增加功能来说,装饰器模式相比使用…...
Anchor Alignment Metric来优化目标检测的标签分配和损失函数。
文章目录 背景假设情况任务和目标TaskAligned方法的应用1. **计算Anchor Alignment Metric**2. **动态样本分配**3. **调整损失函数** 示例总结 背景 假设我们在进行目标检测任务,并且使用了YOLOv8模型。我们希望通过TaskAligned方法来优化Anchor与目标的匹配程度&…...
C++---由优先级队列认识仿函数
文章目录 一、优先级队列是什么? 二、如何使用优先级队列 1、优先级队列容器用法 2、为什么容器本身无序? 三、什么是仿函数? 1. 什么是仿函数? 2. 仿函数的优势 四、仿函数如何使用? 1、重载operator()函数 2、运用第…...
Client访问Server访问慢的原因
1. 网络层面的问题 网络延迟:客户端与服务器之间的地理距离较远(跨ISP、路径次优),导致高网络延迟(如高 RTT 值)。使用 ping 或 traceroute 工具可以帮助定位网络延迟的来源 - mtr: 结合了ping和traceroute功能&#…...
用RPC Performance Inspector 优化你的区块链
目录 什么是RPC? RPC Performance Inspector 是做什么的? 为什么需要这个工具? 如何使用它? 适合谁用? 如何使用? 什么是RPC? RPC Performance Inspector 是一个专门用于测试和分析RPC性能…...
linux如何查看内存条是ddr几代
在 Linux 系统中,可以通过以下几种方法查看内存条的类型和代数(如 DDR3、DDR4 等): 1. 使用 dmidecode 命令 dmidecode 是一个工具,它可以从系统的 DMI 表(也称为 SMBIOS 表)中提取硬件信息&a…...
LeetCode 3153.所有数对中数位差之和:计数
【LetMeFly】3153.所有数对中数位差之和:计数 力扣题目链接:https://leetcode.cn/problems/sum-of-digit-differences-of-all-pairs/ 车尔尼有一个数组 nums ,它只包含 正 整数,所有正整数的数位长度都 相同 。 两个整数的 数位…...
Spring Boot 整合 Sentinel 实现流量控制
在微服务架构中,流量控制是保障系统稳定性和高可用性的关键技术之一。阿里巴巴开源的 Sentinel 是一款面向分布式系统的流量防护组件,旨在从流量控制、熔断降级、系统负载保护等多个维度保障服务的稳定性。本文将详细介绍如何在 Spring Boot 项目中整合 …...
Elasticsearch倒排索引
什么是倒排索引 倒排索引(Inverted Index)是一种将文档中的每个单词映射到包含该单词的文档列表上的数据结构 倒排索引的构建过程 文档1: “我爱吃苹果” 文档2: “我爱吃香蕉” 文档3: “我喜欢苹果和香蕉” 文档分词:将文档中的文本内容…...
速盾:ddos常用防御方法是什么?
DDoS攻击是一种通过向网络资源发送大量请求或大流量数据来使其过载的攻击手段。为了应对这种攻击,常用的防御方法可以分为三个层次:流量清洗、服务器升级和高防CDN。 流量清洗是一种基础的防御手段,它通过过滤和识别恶意流量来阻止DDoS攻击。…...
二分算法入门(简单题)
习题1 704. 二分查找 给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1。 示例 1: 输入: nums [-1,0,3,5,9,12], targ…...
在使用React Hooks中,如何避免状态更新时的性能问题?
在React Hooks中避免状态更新时的性能问题,可以采取以下一些最佳实践: 避免不必要的状态更新: 使用React.memo、useMemo、和useCallback来避免组件或其子组件进行不必要的渲染。 使用useMemo: 对于基于状态或props的复杂计算&…...
Pytest插件pytest-selenium-让自动化测试更简洁
在现代Web应用的开发中,自动化测试成为确保网站质量的重要手段之一。而Pytest插件 pytest-selenium 则为开发者提供了简单而强大的工具,以便于使用Python进行Web应用的自动化测试。本文将深入介绍 pytest-selenium 插件的基本用法和实际案例,…...
视觉语言模型(VLMs)知多少?
最近这几年,自然语言处理和计算机视觉这两大领域真是突飞猛进,让机器不仅能看懂文字,还能理解图片。这两个领域的结合,催生了视觉语言模型,也就是Vision language models (VLMs) ,它们能同时处理视觉信息和…...
重新修改 Qt 项目的 Kit 配置
要重新修改 Qt 项目的 Kit 配置,你可以按照以下步骤进行操作: 1. 打开 Qt Creator 首先,启动 Qt Creator,确保你的项目已经打开。 2. 进入项目设置 在 Qt Creator 中,点击菜单栏的 “Projects” 标签(通…...
【Spring Boot 3】【Web】自定义响应状态码
【Spring Boot 3】【Web】自定义响应状态码 背景介绍开发环境开发步骤及源码工程目录结构背景 软件开发是一门实践性科学,对大多数人来说,学习一种新技术不是一开始就去深究其原理,而是先从做出一个可工作的DEMO入手。但在我个人学习和工作经历中,每次学习新技术总是要花费…...
Locksupport凭证的底层原理
LockSupport的凭证(通常称为“许可”或“permit”)的底层原理主要涉及到Java的Unsafe类以及系统级的线程同步机制。LockSupport是Java 6(JSR166-JUC)引入的一个类,提供了基本的线程同步原语,其核心功能是通…...
Elasticsearch 再次开源
作者:来自 Elastic Shay Banon [D.N.A] Elasticsearch 和 Kibana 可以再次被称为开源了。很难表达这句话让我有多高兴。我真的激动得跳了起来。Elastic 的所有人都是这样的。开源已经融入我的 DNA,也融入了 Elastic 的 DNA。能够再次将 Elasticsearch 称…...
对称密码学
1. 使用OpenSSL 命令行 在 Ubuntu Linux Distribution (发行版)中, OpenSSL 通常可用。当然,如果不可用的话,也可以使用下以下命令安装 OpenSSL: $ sudo apt-get install openssl 安装完后可以使用以下命令检查 OpenSSL 版本&am…...
正则表达式优化建议
文章目录 优化正则表达式代码示例:注意事项: 一些常见的正则表达式陷阱 优化正则表达式是提高文本处理效率和准确性的重要步骤。以下是一些优化正则表达式的方法: 以下是整理归纳后的正则表达式优化技巧: 优化正则表达式 一、预…...
【入坑系列】TiDB 强制索引在不同库下不生效问题
文章目录 背景SQL 优化情况线上SQL运行情况分析怀疑1:执行计划绑定问题?尝试:SHOW WARNINGS 查看警告探索 TiDB 的 USE_INDEX 写法Hint 不生效问题排查解决参考背景 项目中使用 TiDB 数据库,并对 SQL 进行优化了,添加了强制索引。 UAT 环境已经生效,但 PROD 环境强制索…...
在 Nginx Stream 层“改写”MQTT ngx_stream_mqtt_filter_module
1、为什么要修改 CONNECT 报文? 多租户隔离:自动为接入设备追加租户前缀,后端按 ClientID 拆分队列。零代码鉴权:将入站用户名替换为 OAuth Access-Token,后端 Broker 统一校验。灰度发布:根据 IP/地理位写…...
通过MicroSip配置自己的freeswitch服务器进行调试记录
之前用docker安装的freeswitch的,启动是正常的, 但用下面的Microsip连接不上 主要原因有可能一下几个 1、通过下面命令可以看 [rootlocalhost default]# docker exec -it freeswitch fs_cli -x "sofia status profile internal"Name …...
全面解析数据库:从基础概念到前沿应用
在数字化时代,数据已成为企业和社会发展的核心资产,而数据库作为存储、管理和处理数据的关键工具,在各个领域发挥着举足轻重的作用。从电商平台的商品信息管理,到社交网络的用户数据存储,再到金融行业的交易记录处理&a…...
前端调试HTTP状态码
1xx(信息类状态码) 这类状态码表示临时响应,需要客户端继续处理请求。 100 Continue 服务器已收到请求的初始部分,客户端应继续发送剩余部分。 2xx(成功类状态码) 表示请求已成功被服务器接收、理解并处…...
CSS3相关知识点
CSS3相关知识点 CSS3私有前缀私有前缀私有前缀存在的意义常见浏览器的私有前缀 CSS3基本语法CSS3 新增长度单位CSS3 新增颜色设置方式CSS3 新增选择器CSS3 新增盒模型相关属性box-sizing 怪异盒模型resize调整盒子大小box-shadow 盒子阴影opacity 不透明度 CSS3 新增背景属性ba…...
轻量级Docker管理工具Docker Switchboard
简介 什么是 Docker Switchboard ? Docker Switchboard 是一个轻量级的 Web 应用程序,用于管理 Docker 容器。它提供了一个干净、用户友好的界面来启动、停止和监控主机上运行的容器,使其成为本地开发、家庭实验室或小型服务器设置的理想选择…...
13.10 LangGraph多轮对话系统实战:Ollama私有部署+情感识别优化全解析
LangGraph多轮对话系统实战:Ollama私有部署+情感识别优化全解析 LanguageMentor 对话式训练系统架构与实现 关键词:多轮对话系统设计、场景化提示工程、情感识别优化、LangGraph 状态管理、Ollama 私有化部署 1. 对话训练系统技术架构 采用四层架构实现高扩展性的对话训练…...
el-amap-bezier-curve运用及线弧度设置
文章目录 简介示例线弧度属性主要弧度相关属性其他相关样式属性完整示例链接简介 el-amap-bezier-curve 是 Vue-Amap 组件库中的一个组件,用于在 高德地图 上绘制贝塞尔曲线。 基本用法属性path定义曲线的路径,可以是多个弧线段的组合。stroke-weight线条的宽度。stroke…...
多模态学习路线(2)——DL基础系列
目录 前言 一、归一化 1. Layer Normalization (LN) 2. Batch Normalization (BN) 3. Instance Normalization (IN) 4. Group Normalization (GN) 5. Root Mean Square Normalization(RMSNorm) 二、激活函数 1. Sigmoid激活函数(二分类&…...
