Java设计模式——装饰模式
目录
模式动机
模式定义
模式结构
类图
代码分析
示例:动态添加功能的流
组件接口
具体组件
装饰抽象类
具体装饰类
客户端
模式分析
核心思想
动态扩展功能
组合优于继承
优点
动态扩展功能
组合优于继承
代码复用性高
符合开闭原则
缺点
增加系统的复杂性
类的膨胀
复杂的调试
适用环境
动态扩展功能
避免继承带来的类爆炸性增长
高度可定制化的需求
模式应用
输入输出流
GUI 组件
日志记录
模式扩展
多层次装饰
结合其他设计模式
总结
模式动机
一般有两种方式可以实现给一个类或对象增加行为:
- 继承机制,使用继承机制是给现有类添加功能的一种有效途径,通过继承一个现有类可以使得子类在拥有自身方法的同时还拥有父类的方法。但是这种方法是静态的,用户不能控制增加行为的方式和时机。
- 关联机制,即将一个类的对象嵌入另一个对象中,由另一个对象来决定是否调用嵌入对象的行为以便扩展自己的行为,我们称这个嵌入的对象为装饰器(Decorator)
装饰模式以对客户透明的方式动态地给一个对象附加上更多的责任,换言之,客户端并不会觉得对象在装饰前和装饰后有什么不同。装饰模式可以在不需要创造更多子类的情况下,将对象的功能加以扩展。这就是装饰模式的模式动机。
模式定义
装饰(Decorator)模式是一种结构型设计模式,它允许在运行时动态地给一个对象添加新的职责或行为,而无需修改原有的类结构。装饰模式的核心思想是在不改变原有对象的基础上,通过组合的方式将新的职责附加到对象上。
模式结构
装饰模式包含以下几个主要角色:
- Component(组件接口):定义了被装饰对象的接口。
- ConcreteComponent(具体组件):实现了组件接口,定义了具体的组件对象。
- Decorator(装饰抽象类):也实现了组件接口,持有一个对组件对象的引用,并通过组合的方式将新的职责附加到组件对象上。
- ConcreteDecorator(具体装饰类):实现了装饰抽象类,提供了具体的装饰行为。
类图
代码分析
示例:动态添加功能的流
假设我们正在开发一个文本处理系统,需要支持多种文本处理功能,如加密、压缩等。我们可以使用装饰模式来实现这一需求。
组件接口
// 组件接口
interface Stream {void write(String data);
}
具体组件
// 具体组件:文件流
class FileStream implements Stream {@Overridepublic void write(String data) {System.out.println("Writing data to file: " + data);}
}
装饰抽象类
// 装饰抽象类
class StreamDecorator implements Stream {protected Stream stream;public StreamDecorator(Stream stream) {this.stream = stream;}@Overridepublic void write(String data) {stream.write(data);}
}
具体装饰类
// 具体装饰类:加密流
class EncryptedStream extends StreamDecorator {public EncryptedStream(Stream stream) {super(stream);}@Overridepublic void write(String data) {String encryptedData = encrypt(data);super.write(encryptedData);}private String encrypt(String data) {// 加密逻辑return "Encrypted: " + data;}
}// 具体装饰类:压缩流
class CompressedStream extends StreamDecorator {public CompressedStream(Stream stream) {super(stream);}@Overridepublic void write(String data) {String compressedData = compress(data);super.write(compressedData);}private String compress(String data) {// 压缩逻辑return "Compressed: " + data;}
}
客户端
public class DecoratorPatternDemo {public static void main(String[] args) {Stream stream = new FileStream();Stream encryptedStream = new EncryptedStream(stream);Stream compressedStream = new CompressedStream(encryptedStream);compressedStream.write("Hello, World!");}
}
模式分析
核心思想
装饰模式的核心思想是在不改变对象自身的基础上,通过组合的方式将新的职责附加到对象上。装饰模式允许在运行时动态地给一个对象添加新的职责或行为,而无需修改原有的类结构。
动态扩展功能
装饰模式通过装饰抽象类和具体装饰类,可以在运行时动态地扩展对象的功能。具体装饰类通过继承装饰抽象类,并重写其中的方法来添加新的行为。通过这种方式,可以在不修改原有对象的基础上,灵活地扩展对象的功能。
组合优于继承
装饰模式通过组合的方式扩展对象的功能,而不是通过继承。这种方式避免了继承带来的类爆炸性增长问题,并且使得系统更加灵活和可扩展。
优点
动态扩展功能
装饰模式允许在运行时动态地给一个对象添加新的职责或行为,而无需修改原有的类结构。通过装饰模式,可以在不改变对象自身的基础上,灵活地扩展对象的功能。
组合优于继承
装饰模式通过组合的方式扩展对象的功能,而不是通过继承。这种方式避免了继承带来的类爆炸性增长问题,并且使得系统更加灵活和可扩展。
代码复用性高
装饰模式通过组合的方式扩展对象的功能,可以复用已有的组件类和装饰类,从而提高代码的复用性。
符合开闭原则
装饰模式符合开闭原则,即对扩展开放,对修改关闭。通过引入装饰抽象类和具体装饰类,可以在不修改现有代码的情况下添加新的装饰类,从而扩展系统的功能。
缺点
增加系统的复杂性
装饰模式会增加系统的复杂性,因为它引入了额外的抽象类和具体装饰类。对于简单的系统,使用装饰模式可能会显得过于复杂。
类的膨胀
如果需要添加的装饰类很多,可能会导致装饰类的数量增加,从而增加系统的维护成本。
复杂的调试
由于装饰模式通过组合的方式扩展对象的功能,可能会导致对象的层次结构变得复杂,从而增加调试的难度。
适用环境
动态扩展功能
当需要在运行时动态地给一个对象添加新的职责或行为时,可以使用装饰模式。通过装饰模式,可以在不修改对象自身的基础上,灵活地扩展对象的功能。
避免继承带来的类爆炸性增长
当需要扩展对象的功能,但不想使用继承机制时,可以使用装饰模式。通过装饰模式,可以避免继承带来的类爆炸性增长问题,并且使得系统更加灵活和可扩展。
高度可定制化的需求
当需要高度可定制化的需求时,可以使用装饰模式。通过装饰模式,可以在运行时根据用户的需要动态地添加或移除对象的功能。
模式应用
输入输出流
在 Java 中,输入输出流的实现就使用了装饰模式。例如,BufferedInputStream
和 DataInputStream
都是 InputStream
的装饰类,它们通过组合的方式扩展了 InputStream
的功能。
InputStream inputStream = new FileInputStream("file.txt");
InputStream bufferedInputStream = new BufferedInputStream(inputStream);
InputStream dataInputStream = new DataInputStream(bufferedInputStream);
GUI 组件
在图形用户界面(GUI)中,组件的扩展也经常使用装饰模式。例如,一个按钮组件可以通过装饰模式添加新的功能,如添加背景颜色、边框等。
Button button = new Button("Click me");
Button decoratedButton = new BackgroundColorButton(button, Color.BLUE);
Button finalButton = new BorderButton(decoratedButton, BorderStyle.SOLID);
日志记录
在日志记录系统中,可以通过装饰模式动态地添加新的日志记录方式。例如,可以将日志记录到文件、数据库或网络服务器。
Logger logger = new FileLogger();
Logger decoratedLogger = new DatabaseLogger(logger);
Logger finalLogger = new NetworkLogger(decoratedLogger);
模式扩展
多层次装饰
在某些情况下,可能需要多层次的装饰。例如,一个文本处理系统可能需要支持多种文本处理功能,如加密、压缩、编码转换等。通过多层次的装饰,可以将这些不同的功能组合在一起,从而实现高度的灵活性和可扩展性。
Stream stream = new FileStream();
Stream encryptedStream = new EncryptedStream(stream);
Stream compressedStream = new CompressedStream(encryptedStream);
Stream encodedStream = new EncodedStream(compressedStream);
结合其他设计模式
装饰模式可以与其他设计模式结合使用,以进一步提高系统的灵活性和可扩展性。例如:
- 工厂模式:可以使用工厂模式来创建组件对象和装饰对象,从而进一步提高系统的灵活性。
- 策略模式:可以在装饰模式的基础上使用策略模式,动态地选择不同的装饰策略。
- 代理模式:可以在装饰模式的基础上使用代理模式,进一步增强对象的功能。
总结
- 装饰模式用于动态地给一个对象增加一些额外的职责,就增加对象功 能来说,装饰模式比生成子类实现更为灵活。它是一种对象结构型模 式。
- 装饰模式包含四个角色:抽象构件定义了对象的接口,可以给这些对 象动态增加职责(方法);具体构件定义了具体的构件对象,实现了 在抽象构件中声明的方法,装饰器可以给它增加额外的职责(方法); 抽象装饰类是抽象构件类的子类,用于给具体构件增加职责,但是具 体职责在其子类中实现;具体装饰类是抽象装饰类的子类,负责向构 件添加新的职责。
- 使用装饰模式来实现扩展比继承更加灵活,它以对客户透明的方式动 态地给一个对象附加更多的责任。装饰模式可以在不需要创造更多子 类的情况下,将对象的功能加以扩展。
- 装饰模式的主要优点在于可以提供比继承更多的灵活性,可以通过一种动态的 方式来扩展一个对象的功能,并通过使用不同的具体装饰类以及这些装饰类的 排列组合,可以创造出很多不同行为的组合,而且具体构件类与具体装饰类可 以独立变化,用户可以根据需要增加新的具体构件类和具体装饰类;其主要缺 点在于使用装饰模式进行系统设计时将产生很多小对象,而且装饰模式比继承 更加易于出错,排错也很困难,对于多次装饰的对象,调试时寻找错误可能需 要逐级排查,较为烦琐。
- 装饰模式适用情况包括:在不影响其他对象的情况下,以动态、透明的方式给 单个对象添加职责;需要动态地给一个对象增加功能,这些功能也可以动态地 被撤销;当不能采用继承的方式对系统进行扩充或者采用继承不利于系统扩展 和维护时。
- 装饰模式可分为透明装饰模式和半透明装饰模式:在透明装饰模式中,要求客 户端完全针对抽象编程,装饰模式的透明性要求客户端程序不应该声明具体构 件类型和具体装饰类型,而应该全部声明为抽象构件类型;半透明装饰模式允 许用户在客户端声明具体装饰者类型的对象,调用在具体装饰者中新增的方法。
相关文章:
Java设计模式——装饰模式
目录 模式动机 模式定义 模式结构 类图 代码分析 示例:动态添加功能的流 组件接口 具体组件 装饰抽象类 具体装饰类 客户端 模式分析 核心思想 动态扩展功能 组合优于继承 优点 动态扩展功能 组合优于继承 代码复用性高 符合开闭原则 缺点 增加…...
【TouchSocket 和 client.GetStream 区别】
TouchSocket 和 client.GetStream() 是用于网络通信的不同工具和方法,但它们的功能层面和适用范围也有明显区别。下面我来详细解释 TouchSocket 和 client.GetStream() 的差异。 1. TouchSocket TouchSocket 是一个完整的 网络通信框架,专注于为开发者…...

怎么利用商品详情API接口实现数据获取与应用?
在当今数字化的商业时代,高效获取和利用商品数据对于企业和开发者来说至关重要。商品详情 API 接口为我们提供了一种便捷的方式来获取丰富的商品信息,从而实现各种有价值的应用。本文将深入探讨如何利用商品详情 API 接口实现数据获取与应用。 一、商品…...
【AGC005D】~K Perm Counting(计数抽象成图)
容斥原理。 求出f(m) ,f(m)指代至少有m个位置不合法的方案数。 怎么求? 注意到位置为id,权值为v ,不合法的情况,当且仅当 v idk或 v id-k 因此,我们把每一个位置和权值抽象成点 ,不合法的情况之间连一…...
【React】setState (useState) 是怎么记住上一个状态值的?
在 React 中,setState 通过 React 内部的状态管理机制来记住上一个状态值。即使每次组件重新渲染时,函数组件会被重新执行,React 仍能通过其内部的状态管理系统保持和追踪组件的状态变化。下面详细解释其工作原理: 1. setState 的…...
Vue3 使用CryptoJS加密
为什么要加密? 现在的互联网世界充满了各种各样的信息,有些信息非常重要,比如密码、个人信息等。如果我们把这些信息直接发送到服务器,别人可能会截取到,然后偷走我们的信息。为了避免这种情况发生,我们需…...
Feign的使用
一、Feign 介绍 Feign 是一个声明式的 HTTP 客户端,它使得编写 HTTP 客户端变得更加简单。在微服务架构中,使用 Feign 可以轻松地调用其他服务。Feign 内置了 Ribbon 实现负载均衡。 二、Feign 的使用步骤 引入依赖: 在项目的 pom.xml 文件…...

前端反接保护:实用方案解析与探讨
前端反接保护通常采用肖特基二极管方案或PMOS/NMOS方案,本文另外介绍一种理想二极管方案。 1、肖特基二极管方案 由于肖特基二极管具有正向导通电压,只能用于小电流场合,甚至于直接使用普通的整流二极管。比如1A电流,设D1的正向…...

【C++】第五节:内存管理
1、C/C内存分布 看下面一段代码 int globalVar 1; static int staticGlobalVar 1; void Test() {static int staticVar 1;int localVar 1;int num1[10] { 1, 2, 3, 4 };char char2[] "abcd";const char* pChar3 "abcd";int* ptr1 (int*)malloc(s…...

【Java SE】方法 和 递归 的应用
🔥博客主页🔥:【 坊钰_CSDN博客 】 欢迎各位点赞👍评论✍收藏⭐ 目录 1. 方法的含义 1.1 例子 1.2 方法的概念 2. 方法的定义 3. 实参和形参 3.1 实参和形参的关系 4. 方法的重载 5. 递归 5.1 递归练习 6. 小结 1. 方法的…...

JVS低代码轻应用是什么?是如何拼装的?这篇文章讲的非常详细
1.1JVS轻应用是什么? 轻应用与传统应用的开发过程区别 传统开发(原生开发)采用的方式:①需求了解 ②产品原型③UI设计④建库建表⑤前端还原⑥后端开发⑦前后端联调⑧功能测试⑨部署上线轻应用开发方式(配置化拼装&…...
K210(openMV)与STM32 通信教程
目录 前言: 一、K210 串口部分教程 二、STM32部分 前言: 很多打比赛的同学,通常只是用K210 或者openMV来进行视觉部分的信息采集,传输数据给STM32(或者其他主控那边)进行对分析,对小车或者舵…...

【HarmonyOS】HMRouter使用详解(三)生命周期
生命周期(Lifecycle) 使用HMRouter的页面跳转时,想实现和Navigation一样的生命周期时,需要通过新建生命周期类来实现对页面对某一个生命周期的监控。 新建Lifecycle类 通过继承IHMLifecycle接口实现生命周期接口的方法重写。 通过…...
Docker 教程三 (Ubuntu Docker安装)
Ubuntu Docker 安装 Docker Engine-Community 支持以下的 Ubuntu 版本: Xenial 16.04 (LTS)Bionic 18.04 (LTS)Cosmic 18.10Disco 19.04 其他更新的版本…… Docker Engine - Community 支持上 x86_64(或 amd64)armhf,arm64&am…...

Redis:持久化
Redis:持久化 持久化RDBdump.rdb优缺点 AOF文件同步重写机制 混合持久化 持久化 虽然Redis是一个内存级别的数据库,但是Redis也是有持久化的能力的。当系统崩溃时,Redis就会被强制退出,此时内存中的数据就会丢失。为了能够在下次…...
精准监控,高效运营 —— 商品信息实时分析为商家带来新机遇
在现代商业环境中,精准监控和高效运营是商家成功的关键。通过实时分析商品信息,商家可以洞察市场趋势、优化库存管理、提升销售策略,从而抓住新的商业机遇。本文将介绍如何利用Python和一些流行的数据分析工具来实现商品信息的实时分析&#…...
Nginx应用配置实战
Nginx通用部署 Nginx常见参数介绍 Nginx 配置文件中的指令和参数决定了它的行为。下面详细介绍一些常见的 Nginx 参数,以帮助你更好地理解和配置 Nginx。 1. worker_processes worker_processes auto;作用:设置 Nginx 处理请求的工作进程数量。auto …...
html实现倒计时
参考网址 <!DOCTYPE html> <html> <head><title>倒计时示例</title> </head> <body><h1 id"titleCountDown"></h1><div id"countdown"></div><script>// 目标日期var targetDat…...

HTMLCSS练习
1) 效果如下 2) 代码如下 2.1) HTML <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><meta name"viewport" conte…...

LeetCode讲解篇之377. 组合总和 Ⅳ
文章目录 题目描述题解思路题解代码题目链接 题目描述 题解思路 总和为target的元素组合个数 可以由 总和为target - nums[j]的元素组合个数 转换而来,其中j为nums所有元素的下标 而总和target - nums[j]的元素组合个数 可以由 总和为target - nums[j] - nums[k]的…...

Docker 离线安装指南
参考文章 1、确认操作系统类型及内核版本 Docker依赖于Linux内核的一些特性,不同版本的Docker对内核版本有不同要求。例如,Docker 17.06及之后的版本通常需要Linux内核3.10及以上版本,Docker17.09及更高版本对应Linux内核4.9.x及更高版本。…...
java 实现excel文件转pdf | 无水印 | 无限制
文章目录 目录 文章目录 前言 1.项目远程仓库配置 2.pom文件引入相关依赖 3.代码破解 二、Excel转PDF 1.代码实现 2.Aspose.License.xml 授权文件 总结 前言 java处理excel转pdf一直没找到什么好用的免费jar包工具,自己手写的难度,恐怕高级程序员花费一年的事件,也…...

CMake基础:构建流程详解
目录 1.CMake构建过程的基本流程 2.CMake构建的具体步骤 2.1.创建构建目录 2.2.使用 CMake 生成构建文件 2.3.编译和构建 2.4.清理构建文件 2.5.重新配置和构建 3.跨平台构建示例 4.工具链与交叉编译 5.CMake构建后的项目结构解析 5.1.CMake构建后的目录结构 5.2.构…...

【CSS position 属性】static、relative、fixed、absolute 、sticky详细介绍,多层嵌套定位示例
文章目录 ★ position 的五种类型及基本用法 ★ 一、position 属性概述 二、position 的五种类型详解(初学者版) 1. static(默认值) 2. relative(相对定位) 3. absolute(绝对定位) 4. fixed(固定定位) 5. sticky(粘性定位) 三、定位元素的层级关系(z-i…...

第 86 场周赛:矩阵中的幻方、钥匙和房间、将数组拆分成斐波那契序列、猜猜这个单词
Q1、[中等] 矩阵中的幻方 1、题目描述 3 x 3 的幻方是一个填充有 从 1 到 9 的不同数字的 3 x 3 矩阵,其中每行,每列以及两条对角线上的各数之和都相等。 给定一个由整数组成的row x col 的 grid,其中有多少个 3 3 的 “幻方” 子矩阵&am…...

用机器学习破解新能源领域的“弃风”难题
音乐发烧友深有体会,玩音乐的本质就是玩电网。火电声音偏暖,水电偏冷,风电偏空旷。至于太阳能发的电,则略显朦胧和单薄。 不知你是否有感觉,近两年家里的音响声音越来越冷,听起来越来越单薄? —…...

LINUX 69 FTP 客服管理系统 man 5 /etc/vsftpd/vsftpd.conf
FTP 客服管理系统 实现kefu123登录,不允许匿名访问,kefu只能访问/data/kefu目录,不能查看其他目录 创建账号密码 useradd kefu echo 123|passwd -stdin kefu [rootcode caozx26420]# echo 123|passwd --stdin kefu 更改用户 kefu 的密码…...
Go 语言并发编程基础:无缓冲与有缓冲通道
在上一章节中,我们了解了 Channel 的基本用法。本章将重点分析 Go 中通道的两种类型 —— 无缓冲通道与有缓冲通道,它们在并发编程中各具特点和应用场景。 一、通道的基本分类 类型定义形式特点无缓冲通道make(chan T)发送和接收都必须准备好࿰…...
CRMEB 中 PHP 短信扩展开发:涵盖一号通、阿里云、腾讯云、创蓝
目前已有一号通短信、阿里云短信、腾讯云短信扩展 扩展入口文件 文件目录 crmeb\services\sms\Sms.php 默认驱动类型为:一号通 namespace crmeb\services\sms;use crmeb\basic\BaseManager; use crmeb\services\AccessTokenServeService; use crmeb\services\sms\…...

【LeetCode】算法详解#6 ---除自身以外数组的乘积
1.题目介绍 给定一个整数数组 nums,返回 数组 answer ,其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。 题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。 请 不要使用除法,且在 O…...