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

一、什么是装饰器模式
装饰器模式(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…...
正则表达式优化建议
文章目录 优化正则表达式代码示例:注意事项: 一些常见的正则表达式陷阱 优化正则表达式是提高文本处理效率和准确性的重要步骤。以下是一些优化正则表达式的方法: 以下是整理归纳后的正则表达式优化技巧: 优化正则表达式 一、预…...
IDEA运行Tomcat出现乱码问题解决汇总
最近正值期末周,有很多同学在写期末Java web作业时,运行tomcat出现乱码问题,经过多次解决与研究,我做了如下整理: 原因: IDEA本身编码与tomcat的编码与Windows编码不同导致,Windows 系统控制台…...
日语AI面试高效通关秘籍:专业解读与青柚面试智能助攻
在如今就业市场竞争日益激烈的背景下,越来越多的求职者将目光投向了日本及中日双语岗位。但是,一场日语面试往往让许多人感到步履维艰。你是否也曾因为面试官抛出的“刁钻问题”而心生畏惧?面对生疏的日语交流环境,即便提前恶补了…...
【入坑系列】TiDB 强制索引在不同库下不生效问题
文章目录 背景SQL 优化情况线上SQL运行情况分析怀疑1:执行计划绑定问题?尝试:SHOW WARNINGS 查看警告探索 TiDB 的 USE_INDEX 写法Hint 不生效问题排查解决参考背景 项目中使用 TiDB 数据库,并对 SQL 进行优化了,添加了强制索引。 UAT 环境已经生效,但 PROD 环境强制索…...
centos 7 部署awstats 网站访问检测
一、基础环境准备(两种安装方式都要做) bash # 安装必要依赖 yum install -y httpd perl mod_perl perl-Time-HiRes perl-DateTime systemctl enable httpd # 设置 Apache 开机自启 systemctl start httpd # 启动 Apache二、安装 AWStats࿰…...
在web-view 加载的本地及远程HTML中调用uniapp的API及网页和vue页面是如何通讯的?
uni-app 中 Web-view 与 Vue 页面的通讯机制详解 一、Web-view 简介 Web-view 是 uni-app 提供的一个重要组件,用于在原生应用中加载 HTML 页面: 支持加载本地 HTML 文件支持加载远程 HTML 页面实现 Web 与原生的双向通讯可用于嵌入第三方网页或 H5 应…...
深度剖析 DeepSeek 开源模型部署与应用:策略、权衡与未来走向
在人工智能技术呈指数级发展的当下,大模型已然成为推动各行业变革的核心驱动力。DeepSeek 开源模型以其卓越的性能和灵活的开源特性,吸引了众多企业与开发者的目光。如何高效且合理地部署与运用 DeepSeek 模型,成为释放其巨大潜力的关键所在&…...
水泥厂自动化升级利器:Devicenet转Modbus rtu协议转换网关
在水泥厂的生产流程中,工业自动化网关起着至关重要的作用,尤其是JH-DVN-RTU疆鸿智能Devicenet转Modbus rtu协议转换网关,为水泥厂实现高效生产与精准控制提供了有力支持。 水泥厂设备众多,其中不少设备采用Devicenet协议。Devicen…...
【Linux】Linux安装并配置RabbitMQ
目录 1. 安装 Erlang 2. 安装 RabbitMQ 2.1.添加 RabbitMQ 仓库 2.2.安装 RabbitMQ 3.配置 3.1.启动和管理服务 4. 访问管理界面 5.安装问题 6.修改密码 7.修改端口 7.1.找到文件 7.2.修改文件 1. 安装 Erlang 由于 RabbitMQ 是用 Erlang 编写的,需要先安…...
Win系统权限提升篇UAC绕过DLL劫持未引号路径可控服务全检项目
应用场景: 1、常规某个机器被钓鱼后门攻击后,我们需要做更高权限操作或权限维持等。 2、内网域中某个机器被钓鱼后门攻击后,我们需要对后续内网域做安全测试。 #Win10&11-BypassUAC自动提权-MSF&UACME 为了远程执行目标的exe或者b…...
无头浏览器技术:Python爬虫如何精准模拟搜索点击
1. 无头浏览器技术概述 1.1 什么是无头浏览器? 无头浏览器是一种没有图形用户界面(GUI)的浏览器,它通过程序控制浏览器内核(如Chromium、Firefox)执行页面加载、JavaScript渲染、表单提交等操作。由于不渲…...
