当前位置: 首页 > news >正文

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 中,输入输出流的实现就使用了装饰模式。例如,BufferedInputStreamDataInputStream 都是 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]的元素组合个数 转换而来&#xff0c;其中j为nums所有元素的下标 而总和target - nums[j]的元素组合个数 可以由 总和为target - nums[j] - nums[k]的…...

IGP(Interior Gateway Protocol,内部网关协议)

IGP&#xff08;Interior Gateway Protocol&#xff0c;内部网关协议&#xff09; 是一种用于在一个自治系统&#xff08;AS&#xff09;内部传递路由信息的路由协议&#xff0c;主要用于在一个组织或机构的内部网络中决定数据包的最佳路径。与用于自治系统之间通信的 EGP&…...

java调用dll出现unsatisfiedLinkError以及JNA和JNI的区别

UnsatisfiedLinkError 在对接硬件设备中&#xff0c;我们会遇到使用 java 调用 dll文件 的情况&#xff0c;此时大概率出现UnsatisfiedLinkError链接错误&#xff0c;原因可能有如下几种 类名错误包名错误方法名参数错误使用 JNI 协议调用&#xff0c;结果 dll 未实现 JNI 协…...

什么是库存周转?如何用进销存系统提高库存周转率?

你可能听说过这样一句话&#xff1a; “利润不是赚出来的&#xff0c;是管出来的。” 尤其是在制造业、批发零售、电商这类“货堆成山”的行业&#xff0c;很多企业看着销售不错&#xff0c;账上却没钱、利润也不见了&#xff0c;一翻库存才发现&#xff1a; 一堆卖不动的旧货…...

DBAPI如何优雅的获取单条数据

API如何优雅的获取单条数据 案例一 对于查询类API&#xff0c;查询的是单条数据&#xff0c;比如根据主键ID查询用户信息&#xff0c;sql如下&#xff1a; select id, name, age from user where id #{id}API默认返回的数据格式是多条的&#xff0c;如下&#xff1a; {&qu…...

MySQL中【正则表达式】用法

MySQL 中正则表达式通过 REGEXP 或 RLIKE 操作符实现&#xff08;两者等价&#xff09;&#xff0c;用于在 WHERE 子句中进行复杂的字符串模式匹配。以下是核心用法和示例&#xff1a; 一、基础语法 SELECT column_name FROM table_name WHERE column_name REGEXP pattern; …...

【学习笔记】深入理解Java虚拟机学习笔记——第4章 虚拟机性能监控,故障处理工具

第2章 虚拟机性能监控&#xff0c;故障处理工具 4.1 概述 略 4.2 基础故障处理工具 4.2.1 jps:虚拟机进程状况工具 命令&#xff1a;jps [options] [hostid] 功能&#xff1a;本地虚拟机进程显示进程ID&#xff08;与ps相同&#xff09;&#xff0c;可同时显示主类&#x…...

Springboot社区养老保险系统小程序

一、前言 随着我国经济迅速发展&#xff0c;人们对手机的需求越来越大&#xff0c;各种手机软件也都在被广泛应用&#xff0c;但是对于手机进行数据信息管理&#xff0c;对于手机的各种软件也是备受用户的喜爱&#xff0c;社区养老保险系统小程序被用户普遍使用&#xff0c;为方…...

保姆级教程:在无网络无显卡的Windows电脑的vscode本地部署deepseek

文章目录 1 前言2 部署流程2.1 准备工作2.2 Ollama2.2.1 使用有网络的电脑下载Ollama2.2.2 安装Ollama&#xff08;有网络的电脑&#xff09;2.2.3 安装Ollama&#xff08;无网络的电脑&#xff09;2.2.4 安装验证2.2.5 修改大模型安装位置2.2.6 下载Deepseek模型 2.3 将deepse…...

Go 并发编程基础:通道(Channel)的使用

在 Go 中&#xff0c;Channel 是 Goroutine 之间通信的核心机制。它提供了一个线程安全的通信方式&#xff0c;用于在多个 Goroutine 之间传递数据&#xff0c;从而实现高效的并发编程。 本章将介绍 Channel 的基本概念、用法、缓冲、关闭机制以及 select 的使用。 一、Channel…...

解析奥地利 XARION激光超声检测系统:无膜光学麦克风 + 无耦合剂的技术协同优势及多元应用

在工业制造领域&#xff0c;无损检测&#xff08;NDT)的精度与效率直接影响产品质量与生产安全。奥地利 XARION开发的激光超声精密检测系统&#xff0c;以非接触式光学麦克风技术为核心&#xff0c;打破传统检测瓶颈&#xff0c;为半导体、航空航天、汽车制造等行业提供了高灵敏…...