Java二十三种设计模式-装饰器模式(7/23)
装饰器模式:动态扩展功能的灵活之选
引言
装饰器模式(Decorator Pattern)是一种结构型设计模式,用于在不修改对象自身的基础上,通过添加额外的职责来扩展对象的功能。

基础知识,java设计模式总体来说设计模式分为三大类:
(1)创建型模式,共5种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。
(2)结构型模式,共7种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。
(3)行为型模式,共11种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。

第一部分:装饰器模式概述
1.1 定义与用途
装饰器模式的基本定义
装饰器模式是一种设计模式,允许用户在不修改对象自身的情况下,向一个对象添加新的功能。这种模式通过创建一个包装对象,也就是装饰器,来包裹实际对象,从而在不修改实际对象的基础上扩展其功能。

解释为何需要装饰器模式
- 动态添加职责:装饰器模式可以在运行时动态地给对象添加额外的职责。
- 保持对象的透明性:使用装饰器模式不改变对象的接口,因此可以在任何使用原始对象的地方使用装饰过的对象。
- 避免继承的复杂性:相比通过继承来扩展功能,装饰器模式提供了一种更灵活的替代方案,避免了多重继承的问题。
1.2 装饰器模式的组成
抽象组件(Component)
- 定义:定义了一个对象接口,可以给这些对象动态地添加职责。
- 角色:充当具体组件和所有装饰器的公共接口。
具体组件(Concrete Component)
- 定义:定义了抽象组件的具体实现,也就是被装饰的具体对象。
- 角色:提供了具体的行为和数据。
抽象装饰器(Decorator)
- 定义:抽象组件的子类,实现了与抽象组件相同的接口,并持有一个抽象组件的实例。
- 角色:为组件添加职责提供了方法,同时保持接口不变。
具体装饰器(Concrete Decorator)
- 定义:抽象装饰器的子类,实现具体装饰功能。
- 角色:通过实现抽象装饰器的接口,给具体组件添加额外的职责。
客户端(Client)
- 角色:使用抽象组件接口与具体组件和装饰器交互。
- 职责:客户端并不关心对象是具体组件还是装饰器,它通过相同的接口与所有对象交互。
装饰器模式通过将一个对象包装在装饰器对象中来扩展功能,而不需要修改对象的类代码。这种模式提供了一种灵活的方式来扩展或增强对象的行为。在下一部分中,我们将通过Java代码示例来展示装饰器模式的具体实现。
第二部分:装饰器模式的实现
2.1 Java实现示例
以下是使用Java语言实现装饰器模式的一个示例。假设我们有一个简单的咖啡类,我们想要通过装饰器模式来添加不同的调料和装饰。
// 抽象组件
interface Coffee {double cost();String getIngredients();
}// 具体组件
class SimpleCoffee implements Coffee {@Overridepublic double cost() {return 10.0;}@Overridepublic String getIngredients() {return "Simple Coffee";}
}// 抽象装饰器
abstract class CoffeeDecorator implements Coffee {protected Coffee decoratedCoffee;public CoffeeDecorator(Coffee coffee) {this.decoratedCoffee = coffee;}@Overridepublic double cost() {return decoratedCoffee.cost();}@Overridepublic String getIngredients() {return decoratedCoffee.getIngredients();}
}// 具体装饰器
class Milk extends CoffeeDecorator {public Milk(Coffee coffee) {super(coffee);}@Overridepublic double cost() {return super.cost() + 2.0;}@Overridepublic String getIngredients() {return super.getIngredients() + ", Milk";}
}class Whip extends CoffeeDecorator {public Whip(Coffee coffee) {super(coffee);}@Overridepublic double cost() {return super.cost() + 1.5;}@Overridepublic String getIngredients() {return super.getIngredients() + ", Whip";}
}// 客户端代码
public class Client {public static void main(String[] args) {Coffee coffee = new SimpleCoffee();System.out.println("Cost: " + coffee.cost());System.out.println("Ingredients: " + coffee.getIngredients());Coffee milkCoffee = new Milk(coffee);System.out.println("Cost: " + milkCoffee.cost());System.out.println("Ingredients: " + milkCoffee.getIngredients());Coffee whipCoffee = new Whip(milkCoffee);System.out.println("Cost: " + whipCoffee.cost());System.out.println("Ingredients: " + whipCoffee.getIngredients());}
}
在这个示例中,Coffee是抽象组件,SimpleCoffee是具体组件。CoffeeDecorator是抽象装饰器,Milk和Whip是具体装饰器。客户端可以通过装饰器来动态地添加调料和装饰。
2.2 装饰器模式中的角色和职责
抽象组件(Component)
- 职责:定义装饰对象和具体对象的公共接口,允许通过装饰器来扩展功能。
具体组件(Concrete Component)
- 职责:定义一个具体的类,实现抽象组件接口,是装饰器装饰的对象。
抽象装饰器(Decorator)
- 职责:持有一个组件(Component)对象的实例,定义装饰器的接口,继承自抽象组件。
- 实现:通常是一个抽象类,实现接口中的方法,并将调用委托给被装饰的组件。
具体装饰器(Concrete Decorator)
- 职责:实现抽象装饰器的接口,添加装饰的功能。
- 实现:具体装饰器类会持有一个组件对象,并在调用组件方法前后添加额外的行为。
客户端(Client)
- 职责:通过抽象组件接口与具体组件和装饰器交互,客户端不关心对象是具体组件还是装饰器。
装饰器模式通过在运行时动态地添加装饰器来扩展对象的功能,提供了一种灵活且强大的方式来增强对象的行为。在下一部分中,我们将探讨装饰器模式的使用场景。

第三部分:装饰器模式的使用场景
3.1 动态添加职责
在软件开发中,经常会遇到需要在运行时动态地给对象添加额外职责的情况。装饰器模式提供了一种灵活的方式来实现这一点,而无需改变对象本身的结构。
何时需要动态地给对象添加职责:
- 功能扩展:当需要为对象添加新功能,但又不想通过继承来扩展现有类时。
- 条件性行为:某些行为只在特定条件下需要,装饰器模式可以根据运行时的条件来决定是否添加这些行为。
- 多样化组合:需要多种行为组合时,装饰器模式允许通过组合不同的装饰器来实现。
应用实例:
- GUI组件:在图形用户界面中,可能需要为组件动态添加如边框、颜色、阴影等视觉效果。
- 日志记录:在不修改现有方法的情况下,为方法动态添加日志记录功能。
3.2 避免使用多重继承
Java等许多编程语言不支持多重继承,这限制了通过继承来扩展对象功能的方式。装饰器模式提供了一种替代方案,允许一个对象在运行时拥有多个“继承”自不同类的行为。
在需要避免多重继承的情况下,装饰器模式的优势:
- 解决多重继承问题:装饰器模式允许对象在运行时拥有多个职责,而不需要通过多重继承实现。
- 灵活性:装饰器模式提供了更高的灵活性,可以在运行时动态地添加或移除职责。
- 保持类的独立性:避免了因为多重继承导致的类之间的强耦合。
应用实例:
- 支付系统:在一个支付系统中,可能需要为支付方法添加日志记录、事务管理、安全性检查等多种功能。使用装饰器模式可以避免创建过多的继承层级。
- 网络通信:在网络通信中,可能需要为通信协议添加加密、压缩、认证等多种功能。装饰器模式允许在不修改协议本身的情况下,动态地添加这些功能。
装饰器模式是一种非常有用的设计模式,它允许开发者在不修改对象结构的前提下,动态地扩展对象的功能。在实际应用中,根据具体需求和场景选择是否使用装饰器模式是非常重要的。在下一部分中,我们将讨论装饰器模式的优点与缺点。

第四部分:装饰器模式的优点与缺点
4.1 优点
提高灵活性
- 动态添加行为:装饰器模式可以在运行时动态地给对象添加额外的行为或职责。
增强可扩展性
- 无需修改原有代码:通过装饰器可以扩展功能,而无需修改原有的类代码,遵循开闭原则。
简化对象复用
- 减少类的数量:避免创建多个继承层次,减少系统中类的数量,简化复用。
提供更好的封装性
- 隐藏实现细节:装饰器模式将对象的具体实现细节封装在装饰器内部,对外提供统一的接口。
4.2 缺点
过度使用导致复杂性
- 多层装饰器嵌套:如果过度使用装饰器模式,可能会导致多层嵌套,难以理解和维护。
增加系统的复杂度
- 类结构复杂:装饰器模式可能会使系统的类结构变得更加复杂,尤其是当有多个装饰器时。
性能问题
- 可能影响性能:在某些情况下,装饰器模式可能会引入额外的性能开销,尤其是在装饰器较多的情况下。

第五部分:装饰器模式与其他模式的比较
5.1 与适配器模式的比较
适配器模式
- 目的:使不兼容的接口能够一起工作。
- 实现:通常通过继承或组合来转换一个类的接口。
装饰器模式
- 目的:动态地给对象添加额外的职责。
- 实现:通过组合来包装对象,增加新的行为。
对比
- 目的不同:适配器模式主要用于接口转换,而装饰器模式用于功能扩展。
- 使用场景:适配器模式适用于解决接口不兼容的问题,装饰器模式适用于动态添加职责。
5.2 与外观模式的对比
外观模式
- 目的:提供一个统一的接口来访问子系统中的一组接口。
- 实现:通过一个外观类来简化客户端与复杂系统的交互。
装饰器模式
- 目的:动态地给对象添加额外的职责。
对比
- 简化接口:外观模式用于简化客户端与复杂系统之间的接口,而装饰器模式用于在不修改对象的情况下扩展功能。
- 使用场景:外观模式适用于简化系统访问,装饰器模式适用于功能增强。
装饰器模式提供了一种灵活的方式来动态地扩展对象的功能,但也需要谨慎使用,以避免增加系统的复杂性和维护难度。在实际应用中,根据具体需求和场景选择合适的设计模式是非常重要的。在下一部分中,我们将提供装饰器模式的最佳实践和建议。

第六部分:装饰器模式的最佳实践和建议
6.1 最佳实践
保持接口的一致性
- 统一接口:确保所有装饰器和被装饰的组件都实现同一个接口,这样客户端就可以以统一的方式处理它们。
装饰者应该是非侵入性的
- 不修改原始类:装饰者不应该修改原始类代码,以保证原始类的独立性和可重用性。
单一职责原则
- 单一职责:每个装饰者应该只关注添加一种职责,遵循单一职责原则。
使用装饰者工厂
- 工厂模式:使用工厂模式来创建装饰者,这样可以更好地控制对象的创建过程。
避免过度装饰
- 适度使用:避免创建过多的装饰者,以免系统变得复杂难以管理。
6.2 避免滥用
避免多层装饰器导致的复杂性
- 简化设计:避免不必要的多层嵌套装饰,保持设计的简洁性。
避免过度复杂的继承结构
- 扁平化设计:尽量保持扁平化的设计,避免创建过多的装饰者类。
考虑使用组合而非继承
- 组合优于继承:在某些情况下,使用对象组合代替继承可能更灵活。
6.3 替代方案
#.# 使用组合模式
- 定义:组合模式允许将对象组合成树形结构,以表示“部分-整体”的层次结构。
- 适用场景:当需要表示对象之间的层次关系时。
使用策略模式
- 定义:策略模式定义了一系列算法,并将每一个算法封装起来,使它们可以互换。
- 适用场景:当需要根据不同的策略动态改变对象行为时。
使用外观模式
- 适用场景:当需要简化客户端对复杂系统的访问时。
使用享元模式
- 定义:享元模式通过共享来高效地支持大量细粒度对象的复用。
- 适用场景:当系统中存在大量相似对象时,可以通过享元模式来减少内存消耗。
装饰器模式是一种强大的设计模式,可以提供灵活性和可扩展性,但也需要谨慎使用以避免复杂性和性能问题。了解其替代方案可以帮助开发者根据具体需求和场景选择最合适的设计模式。在实际开发中,应根据具体情况灵活运用装饰器模式,以达到最佳的设计效果。

结语
装饰器模式提供了一种灵活的方式来扩展对象的功能,而无需改变对象的结构。通过本文的深入分析,希望读者能够对装饰器模式有更全面的理解,并在实际开发中做出合理的设计选择。
相关Java设计模式文章:
Java二十三种设计模式-单例模式(1/23)
Java二十三种设计模式-工厂方法模式(2/23)
Java二十三种设计模式-抽象工厂模式(3/23)
Java二十三种设计模式-建造者模式(4/23)
Java二十三种设计模式-原型模式(5/23)
Java二十三种设计模式-适配器模式(6/23)
Java二十三种设计模式-装饰器模式(7/23)
相关文章:
Java二十三种设计模式-装饰器模式(7/23)
装饰器模式:动态扩展功能的灵活之选 引言 装饰器模式(Decorator Pattern)是一种结构型设计模式,用于在不修改对象自身的基础上,通过添加额外的职责来扩展对象的功能。 基础知识,java设计模式总体来说设计…...
正则表达式与文本处理
目录 一、正则表达式 1、正则表达式定义 1.1正则表达式的概念及作用 1.2、正则表达式的工具 1.3、正则表达式的组成 2、基础正则表达式 3、扩展正则表达式 4、元字符操作 4.1、查找特定字符 4.2、利用中括号“[]”来查找集合字符 4.3、查找行首“^”与行尾字符“$”…...
Python | Leetcode Python题解之第283题移动零
题目: 题解: class Solution:def moveZeroes(self, nums: List[int]) -> None:n len(nums)left right 0while right < n:if nums[right] ! 0:nums[left], nums[right] nums[right], nums[left]left 1right 1...
微信小程序面试题汇总
面试题 1. 请简述微信小程序主要目录和文件的作用? 参考回答: 微信小程序主要目录和文件的作用:(1)project.config.json:项目配置文件,用的最多的就是配置是否开启https校验 (2&am…...
学习日志:JVM垃圾回收
文章目录 前言一、堆空间的基本结构二、内存分配和回收原则对象优先在 Eden 区分配大对象直接进入老年代长期存活的对象将进入老年代主要进行 gc 的区域空间分配担保 三、死亡对象判断方法引用计数法可达性分析算法引用类型总结1.强引用(StrongReference…...
Vue前端页面嵌入mermaid图表--流程图
一、安装Mermaid 首先,你需要在你的项目中安装Mermaid。可以通过npm或yarn来安装: npm install mermaid --save # 或者 yarn add mermaid结果如图: 二、Vue 方法一:使用pre标签 使用ref属性可以帮助你在Vue组件中访问DOM元素 …...
【web]-反序列化-easy ? not easy
打开后看到源码 <?php error_reporting(0); highlight_file(__FILE__);class A{public $class;public $para;public $check;public function __construct(){$this->class "B";$this->para "ctfer";echo new $this->class ($this->para…...
python 内置函数、math模块
一、内置函数 内置函数是 Python 解释器内置的一组函数,它们可以直接在 Python 程序中使用,无需额外导入模块。这些内置函数提供了基本的操作和功能,涵盖了广泛的用途,从数学运算到数据结构操作等等。 import mathprint(type(10)…...
Ubuntu Docker 安装
Ubuntu Docker 安装 1. 引言 Docker 是一个开源的应用容器引擎,它允许开发者打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的 Linux 机器上,也可以实现虚拟化。容器是完全使用沙箱机制,相互之间不会有任何接口。 2. 系统要求 在安装 Docker 之前,…...
vue接入google map自定义marker教程
需求背景 由于客户需求,原来系统接入的高德地图,他们不接受,需要换成google地图。然后就各种百度,各种Google,却不能实现。----无语,就连google地图官方的api也是一坨S-H-I。所以才出现这篇文章。 google地…...
Spring Boot集成Redis与Lua脚本:构建高效的分布式多规则限流系统
文章目录 Redis多规则限流和防重复提交记录访问次数解决临界值访问问题实现多规则限流先确定最终需要的效果编写注解(RateLimiter,RateRule)拦截注解 RateLimiter 编写lua脚本UUID时间戳编写 AOP 拦截 总结 Redis多规则限流和防重复提交 市面…...
四、单线程多路IO复用+多线程业务工作池
文章目录 一、前言1 编译方法 二、单线程多路IO复用多线程业务工作池结构三、重写Client_Context类四、编写Server类 一、前言 我们以及讲完单线程多路IO复用 以及任务调度与执行的C线程池,接下来我们就给他结合起来。 由于项目变大,尝试解耦项目&#…...
单元测试--Junit
Junit是Java的单元测试框架提供了一些注解方便我们进行单元测试 1. 常用注解 常用注解: TestBeforeAll,AfterAllBeforeEach,AfterEach 使用这些注解需要先引入依赖: <dependency><groupId>org.junit.jupiter<…...
达梦数据库系列—30. DTS迁移Mysql到DM
目录 1.MySQL 源端信息 2.DM 目的端信息 3.迁移评估 4.数据库迁移 4.1源端 MySQL 准备 4.2目的端达梦准备 初始化参数设置 兼容性参数设置 创建迁移用户和表空间 4.3迁移步骤 创建迁移 配置迁移对象及策略 开始迁移 对象补迁 5.数据校验 统计 MySQL 端对象及数…...
随记0000——从0、1 到 C语言
C语言的发展历程是计算机科学史上的一个重要里程碑。 下面是从最早的机器语言到汇编语言,再到高级语言如 C 语言的简化演进过程: 1. 机器语言 定义与特点 机器语言是最底层的编程语言,由一系列二进制代码组成。直接被CPU执行,…...
C++ | Leetcode C++题解之第264题丑数II
题目: 题解: class Solution { public:int nthUglyNumber(int n) {vector<int> dp(n 1);dp[1] 1;int p2 1, p3 1, p5 1;for (int i 2; i < n; i) {int num2 dp[p2] * 2, num3 dp[p3] * 3, num5 dp[p5] * 5;dp[i] min(min(num2, num3…...
前端系列-8 集中式状态管理工具pinia
集中式状态管理工具—pinia vue3中使用pinia作为集中式状态管理工具,替代vue2中的vuex。 pinia文档可参考: https://pinia.web3doc.top/introduction.html 1.项目集成pinia 安装pinia依赖: npm install pinia在main.ts中引入pinia import { createApp } from vu…...
pytest使用
主要技术内容 1.pytest设计 接口测试 框架设想 common—公共的东西封装 1.request请求 2.Session 3.断言 4.Log 5.全局变量 6.shell命令 ❖ config---配置文件及读取 ❖ Log— ❖ payload—请求参数—*.yaml及读取 ❖ testcases—conftest.py; testcase1.py…….可…...
单表查询总结与多表查询概述
1. 单表查询总结 执行顺序: 从一张表,过滤数据,进行分组,对分组后的数据再过滤,查询出来所需数据,排序之后输出; from > where > group by > having > select > order by 2. …...
redis的使用场景和持久化方式
redis的使用场景 热点数据的缓存。热点:频繁读取的数据。限时任务的操作:短信验证码。完成session共享的问题完成分布式锁。 redis的持久化方式 什么是持久化:把内存中的数据存储到磁盘的过程,同时也可以把磁盘中的数据加载到内存…...
LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明
LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造,完美适配AGV和无人叉车。同时,集成以太网与语音合成技术,为各类高级系统(如MES、调度系统、库位管理、立库等)提供高效便捷的语音交互体验。 L…...
.Net框架,除了EF还有很多很多......
文章目录 1. 引言2. Dapper2.1 概述与设计原理2.2 核心功能与代码示例基本查询多映射查询存储过程调用 2.3 性能优化原理2.4 适用场景 3. NHibernate3.1 概述与架构设计3.2 映射配置示例Fluent映射XML映射 3.3 查询示例HQL查询Criteria APILINQ提供程序 3.4 高级特性3.5 适用场…...
c++ 面试题(1)-----深度优先搜索(DFS)实现
操作系统:ubuntu22.04 IDE:Visual Studio Code 编程语言:C11 题目描述 地上有一个 m 行 n 列的方格,从坐标 [0,0] 起始。一个机器人可以从某一格移动到上下左右四个格子,但不能进入行坐标和列坐标的数位之和大于 k 的格子。 例…...
Psychopy音频的使用
Psychopy音频的使用 本文主要解决以下问题: 指定音频引擎与设备;播放音频文件 本文所使用的环境: Python3.10 numpy2.2.6 psychopy2025.1.1 psychtoolbox3.0.19.14 一、音频配置 Psychopy文档链接为Sound - for audio playback — Psy…...
Module Federation 和 Native Federation 的比较
前言 Module Federation 是 Webpack 5 引入的微前端架构方案,允许不同独立构建的应用在运行时动态共享模块。 Native Federation 是 Angular 官方基于 Module Federation 理念实现的专为 Angular 优化的微前端方案。 概念解析 Module Federation (模块联邦) Modul…...
CRMEB 框架中 PHP 上传扩展开发:涵盖本地上传及阿里云 OSS、腾讯云 COS、七牛云
目前已有本地上传、阿里云OSS上传、腾讯云COS上传、七牛云上传扩展 扩展入口文件 文件目录 crmeb\services\upload\Upload.php namespace crmeb\services\upload;use crmeb\basic\BaseManager; use think\facade\Config;/*** Class Upload* package crmeb\services\upload* …...
今日学习:Spring线程池|并发修改异常|链路丢失|登录续期|VIP过期策略|数值类缓存
文章目录 优雅版线程池ThreadPoolTaskExecutor和ThreadPoolTaskExecutor的装饰器并发修改异常并发修改异常简介实现机制设计原因及意义 使用线程池造成的链路丢失问题线程池导致的链路丢失问题发生原因 常见解决方法更好的解决方法设计精妙之处 登录续期登录续期常见实现方式特…...
Web 架构之 CDN 加速原理与落地实践
文章目录 一、思维导图二、正文内容(一)CDN 基础概念1. 定义2. 组成部分 (二)CDN 加速原理1. 请求路由2. 内容缓存3. 内容更新 (三)CDN 落地实践1. 选择 CDN 服务商2. 配置 CDN3. 集成到 Web 架构 …...
听写流程自动化实践,轻量级教育辅助
随着智能教育工具的发展,越来越多的传统学习方式正在被数字化、自动化所优化。听写作为语文、英语等学科中重要的基础训练形式,也迎来了更高效的解决方案。 这是一款轻量但功能强大的听写辅助工具。它是基于本地词库与可选在线语音引擎构建,…...
招商蛇口 | 执笔CID,启幕低密生活新境
作为中国城市生长的力量,招商蛇口以“美好生活承载者”为使命,深耕全球111座城市,以央企担当匠造时代理想人居。从深圳湾的开拓基因到西安高新CID的战略落子,招商蛇口始终与城市发展同频共振,以建筑诠释对土地与生活的…...
