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

一、什么是装饰器模式
装饰器模式(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…...
正则表达式优化建议
文章目录 优化正则表达式代码示例:注意事项: 一些常见的正则表达式陷阱 优化正则表达式是提高文本处理效率和准确性的重要步骤。以下是一些优化正则表达式的方法: 以下是整理归纳后的正则表达式优化技巧: 优化正则表达式 一、预…...
linux之kylin系统nginx的安装
一、nginx的作用 1.可做高性能的web服务器 直接处理静态资源(HTML/CSS/图片等),响应速度远超传统服务器类似apache支持高并发连接 2.反向代理服务器 隐藏后端服务器IP地址,提高安全性 3.负载均衡服务器 支持多种策略分发流量…...
Spark 之 入门讲解详细版(1)
1、简介 1.1 Spark简介 Spark是加州大学伯克利分校AMP实验室(Algorithms, Machines, and People Lab)开发通用内存并行计算框架。Spark在2013年6月进入Apache成为孵化项目,8个月后成为Apache顶级项目,速度之快足见过人之处&…...
边缘计算医疗风险自查APP开发方案
核心目标:在便携设备(智能手表/家用检测仪)部署轻量化疾病预测模型,实现低延迟、隐私安全的实时健康风险评估。 一、技术架构设计 #mermaid-svg-iuNaeeLK2YoFKfao {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg…...
循环冗余码校验CRC码 算法步骤+详细实例计算
通信过程:(白话解释) 我们将原始待发送的消息称为 M M M,依据发送接收消息双方约定的生成多项式 G ( x ) G(x) G(x)(意思就是 G ( x ) G(x) G(x) 是已知的)࿰…...
基于当前项目通过npm包形式暴露公共组件
1.package.sjon文件配置 其中xh-flowable就是暴露出去的npm包名 2.创建tpyes文件夹,并新增内容 3.创建package文件夹...
【配置 YOLOX 用于按目录分类的图片数据集】
现在的图标点选越来越多,如何一步解决,采用 YOLOX 目标检测模式则可以轻松解决 要在 YOLOX 中使用按目录分类的图片数据集(每个目录代表一个类别,目录下是该类别的所有图片),你需要进行以下配置步骤&#x…...
大语言模型(LLM)中的KV缓存压缩与动态稀疏注意力机制设计
随着大语言模型(LLM)参数规模的增长,推理阶段的内存占用和计算复杂度成为核心挑战。传统注意力机制的计算复杂度随序列长度呈二次方增长,而KV缓存的内存消耗可能高达数十GB(例如Llama2-7B处理100K token时需50GB内存&a…...
React---day11
14.4 react-redux第三方库 提供connect、thunk之类的函数 以获取一个banner数据为例子 store: 我们在使用异步的时候理应是要使用中间件的,但是configureStore 已经自动集成了 redux-thunk,注意action里面要返回函数 import { configureS…...
iview框架主题色的应用
1.下载 less要使用3.0.0以下的版本 npm install less2.7.3 npm install less-loader4.0.52./src/config/theme.js文件 module.exports {yellow: {theme-color: #FDCE04},blue: {theme-color: #547CE7} }在sass中使用theme配置的颜色主题,无需引入,直接可…...
Qemu arm操作系统开发环境
使用qemu虚拟arm硬件比较合适。 步骤如下: 安装qemu apt install qemu-system安装aarch64-none-elf-gcc 需要手动下载,下载地址:https://developer.arm.com/-/media/Files/downloads/gnu/13.2.rel1/binrel/arm-gnu-toolchain-13.2.rel1-x…...
