装饰器模式
概述
当我们编写软件时,有时我们会遇到需要在不修改现有代码的情况下添加新功能的情况。这时,我们可以使用装饰器模式。
装饰器模式是一种结构性设计模式,它允许我们在不改变对象接口的情况下动态地向对象添加功能。装饰器模式通过创建一个包装对象来实现这一目的。这个包装对象具有与原始对象相同的接口,但可以通过添加或覆盖方法来扩展或修改其行为。
装饰器模式通常用于以下情况:
- 在运行时动态地向对象添加新功能,而不会影响其他对象。
- 在不改变现有代码的情况下添加新功能。
- 以层次结构方式组合对象以获得更大的灵活性。
代码示例
下面是一个使用Java实现的装饰器模式的示例,以模拟咖啡店销售咖啡的过程。
首先,我们定义一个接口 Coffee,表示咖啡的基本特性,包括名称和价格:
public interface Coffee {String getDescription();double getCost();
}
然后,我们定义一个具体的咖啡类 SimpleCoffee,它实现了 Coffee 接口:
public class SimpleCoffee implements Coffee {@Overridepublic String getDescription() {return "Simple coffee";}@Overridepublic double getCost() {return 1.0;}
}
这个类表示最简单的咖啡,没有任何装饰。
接下来,我们定义一个装饰器类 CoffeeDecorator,它也实现了 Coffee 接口,但是它包含一个 Coffee 类型的成员变量 decoratedCoffee:
public abstract class CoffeeDecorator implements Coffee {protected Coffee decoratedCoffee;public CoffeeDecorator(Coffee decoratedCoffee) {this.decoratedCoffee = decoratedCoffee;}@Overridepublic String getDescription() {return decoratedCoffee.getDescription();}@Overridepublic double getCost() {return decoratedCoffee.getCost();}
}
这个类的作用是让子类可以方便地覆盖 getDescription() 和 getCost() 方法,以添加额外的装饰。
接下来,我们定义两个具体的装饰器类:Milk 和 Whip,它们分别添加牛奶和奶泡:
public class Milk extends CoffeeDecorator {public Milk(Coffee decoratedCoffee) {super(decoratedCoffee);}@Overridepublic String getDescription() {return decoratedCoffee.getDescription() + ", Milk";}@Overridepublic double getCost() {return decoratedCoffee.getCost() + 0.5;}
}public class Whip extends CoffeeDecorator {public Whip(Coffee decoratedCoffee) {super(decoratedCoffee);}@Overridepublic String getDescription() {return decoratedCoffee.getDescription() + ", Whip";}@Overridepublic double getCost() {return decoratedCoffee.getCost() + 0.7;}
}
这些类都是 CoffeeDecorator 的子类,它们实现了自己的 getDescription() 和 getCost() 方法来添加额外的功能。
最后,我们可以使用这些类来创建不同类型的咖啡,例如:
Coffee simpleCoffee = new SimpleCoffee();
System.out.println(simpleCoffee.getDescription() + " $" + simpleCoffee.getCost());Coffee coffeeWithMilk = new Milk(new SimpleCoffee());
System.out.println(coffeeWithMilk.getDescription() + " $" + coffeeWithMilk.getCost());Coffee coffeeWithMilkAndWhip = new Whip(new Milk(new SimpleCoffee()));
System.out.println(coffeeWithMilkAndWhip.getDescription() + " $" + coffeeWithMilkAndWhip.getCost());
JDK中的应用
在 Java IO 库中,许多类都使用了装饰器模式来提供更高级的功能。这些类都是在基本的输入和输出类之上建立的,通过使用不同的装饰器来组合它们,从而实现了各种不同的功能。
例如,BufferedReader 和 BufferedWriter 类就是基于装饰器模式实现的。BufferedReader 类是 Reader 类的一个装饰器,它提供了缓冲输入的功能。当我们需要从一个输入流中读取数据时,BufferedReader 将输入流中的数据读取到缓冲区中,然后逐个字符地返回它们。这样做的好处是可以减少系统调用,从而提高效率。
以下是一个使用 BufferedReader 类的示例:
FileReader fileReader = new FileReader("file.txt");
BufferedReader bufferedReader = new BufferedReader(fileReader);String line;
while ((line = bufferedReader.readLine()) != null) {System.out.println(line);
}bufferedReader.close();
fileReader.close();
在这个例子中,我们首先创建了一个 FileReader 对象来读取文件,然后将其传递给 BufferedReader 的构造函数。BufferedReader 对象使用 FileReader 对象作为其输入源,并提供了缓冲输入的功能。在 while 循环中,我们使用 readLine() 方法逐行读取文件,并打印每一行。
类似的,BufferedWriter 类也是 Writer 类的一个装饰器,它提供了缓冲输出的功能。当我们需要将数据写入到一个输出流中时,BufferedWriter 将数据写入到缓冲区中,直到缓冲区满或者我们调用 flush() 方法时,才将缓冲区中的数据写入到输出流中。
以下是一个使用 BufferedWriter 类的示例:
FileWriter fileWriter = new FileWriter("file.txt");
BufferedWriter bufferedWriter = new BufferedWriter(fileWriter);bufferedWriter.write("Hello, world!");
bufferedWriter.newLine();
bufferedWriter.write("How are you?");bufferedWriter.close();
fileWriter.close();
在这个例子中,我们首先创建了一个 FileWriter 对象来写入文件,然后将其传递给 BufferedWriter 的构造函数。BufferedWriter 对象使用 FileWriter 对象作为其输出目标,并提供了缓冲输出的功能。我们使用 write() 方法将字符串写入到缓冲区中,并使用 newLine() 方法添加一个新行。在最后,我们调用 close() 方法来关闭 BufferedWriter 和 FileWriter 对象。
通过使用装饰器模式,Java IO 库中的类可以轻松地组合成各种不同的输入和输出组合,以提供更高级的功能。
类图
当使用装饰器模式时,通常需要定义以下四种角色:
抽象组件(Component):定义了被装饰对象的基本接口。可以是一个抽象类或者接口。在 Java 中,它通常是一个接口。
具体组件(Concrete Component):实现了抽象组件接口的具体对象,也是被装饰的对象。
抽象装饰器(Decorator):维持一个指向抽象组件对象的引用,并定义了与抽象组件接口一致的接口。
具体装饰器(Concrete Decorator):向装饰对象添加额外的职责或行为。

其中,Component 定义了被装饰对象的基本接口。ConcreteComponent 实现了 Component 接口,也就是被装饰的对象。Decorator 是一个抽象类,它维持一个指向 Component 对象的引用,并定义了与 Component 接口一致的接口。ConcreteDecoratorA 和 ConcreteDecoratorB 分别是具体的装饰器类,它们继承自 Decorator 类并向被装饰对象添加额外的职责或行为。
通过组合不同的具体组件和具体装饰器,可以创建出许多不同的对象,从而提供了灵活而且可扩展的设计。
相关文章:
装饰器模式
概述 当我们编写软件时,有时我们会遇到需要在不修改现有代码的情况下添加新功能的情况。这时,我们可以使用装饰器模式。 装饰器模式是一种结构性设计模式,它允许我们在不改变对象接口的情况下动态地向对象添加功能。装饰器模式通过创建一个…...
21 Nacos客户端本地缓存及故障转移
Nacos客户端本地缓存及故障转移 在Nacos本地缓存的时候有的时候必然会出现一些故障,这些故障就需要进行处理,涉及到的核心类为ServiceInfoHolder和FailoverReactor。 本地缓存有两方面,第一方面是从注册中心获得实例信息会缓存在内存当中&a…...
遍历读取文件夹下的所有文件
遍历读取文件夹下的所有文件 例如,读取文件夹下,子文件夹的所有的jpg文件: import glob path "./database/20230302/night/*/*.jpg"#设置自己的文件夹路径以及文件 image_files glob.glob(path, recursiveTrue)for image_file …...
nexus安装与入门
安装 nexus-3.31.1-01-unix.tar.gz 链接:https://pan.baidu.com/s/1YrJMwpGxmu8N2d7XMl6fSg 提取码:kfeh 上传到服务器,解压 tar -zvxf nexus-3.31.1-01-unix.tar.gz进入bin目录,启动 ./nexus start查看状态 ./nexus status默…...
Flink SQL Checkpoint 学习总结
前言 学习总结Flink SQL Checkpoint的使用,主要目的是为了验证Flink SQL流式任务挂掉后,重启时还可以继续从上次的运行状态恢复。 验证方式 Flink SQL流式增量读取Hudi表然后sink MySQL表,任务启动后处于running状态,先查看sin…...
2023年“楚怡杯“湖南省职业院校技能竞赛“网络安全”竞赛任务书
2023年“楚怡杯“湖南省职业院校技能竞赛“网络安全”竞赛任务书 一、竞赛时间 总计:360分钟 竞赛阶段竞赛阶段 任务阶段 竞赛任务 竞赛时间 分值 A模块 A-1 登录安全加固 180分钟 200分 A-2 本地安全策略配置 A-3 流量完整性保护 A-4 事件监控 …...
MyBatis中主键回填的两种实现方式
主键回填其实是一个非常常见的需求,特别是在数据添加的过程中,我们经常需要添加完数据之后,需要获取刚刚添加的数据 id,无论是 Jdbc 还是各种各样的数据库框架都对此提供了相关的支持,本文我就来和和大家分享下数据库主…...
Windows11如何打开ie浏览器
目录1.背景:2.方法一:在 edge 中配置使用 ie 模式3.方法二:通过 Internet 选项 打开1.背景: 昨天电脑自动从win10升级为win11了,突然发现电脑找不到ie浏览器了,打开全都是直接跳转到 edge 浏览器࿰…...
Linux:进程间通信
目录 进程间通信目的 进程间通信分类 管道 System V IPC POSIX IPC 什么是管道 站在文件描述符角度-深度理解管道 管道使用 管道通信的四种情况 管道通信的特点 进程池管理 命名管道 创建一个命名管道 命名管道的打开规则 命名管道通信实例 匿名管道与命名管道的…...
【java】将LAC改造成Elasticsearch分词插件
目录 为什么要将LAC改造成ES插件? 怎么将LAC改造成ES插件? 确认LAC java接口能work 搭建ES插件开发调试环境 编写插件 生成插件 安装、运行插件 linux版本的动态链接库生成 总结 参考文档 为什么要将LAC改造成ES插件? ES是著名的非…...
TPM 2.0实例探索3 —— LUKS磁盘加密(5)
接前文:TPM 2.0实例探索3 —— LUKS磁盘加密(4) 本文大部分内容参考: Code Sample: Protecting secret data and keys using Intel Platform... 二、LUKS磁盘加密实例 4. 将密码存储于TPM的PCR 现在将TPM非易失性存储器中保护…...
mybatisplus复习(黑马)
学习目标能够基于MyBatisPlus完成标准Dao开发能够掌握MyBatisPlus的条件查询能够掌握MyBatisPlus的字段映射与表名映射能够掌握id生成策略控制能够理解代码生成器的相关配置一、MyBatisPlus简介MyBatisPlus(简称MP)是基于MyBatis框架基础上开发的增强型工…...
【数据聚类|深度聚类】Deep Comprehensive Correlation Mining for Image Clustering(DCCM)论文研读
Abstract 翻译 最近出现的深度无监督方法使我们能够联合学习表示和对未标记数据进行聚类。这些深度聚类方法主要关注样本之间的相关性,例如选择高精度对来逐步调整特征表示,而忽略了其他有用的相关性。本文提出了一种新的聚类框架,称为深度全面相关挖掘(DCCM),从三个方面…...
CE认证机构有哪些机构?
CE认证机构有哪些机构? 所有出口欧盟的产品都需要办理CE证明,而电子电器以及玩具是强制性要做CE认证。很多人以为只有办理欧盟NB公告机构的CE认证才可以被承认,实际上并不是。那么,除了NB公告上的机构,还有哪些认证机…...
MYSQL5.7:Access denied for user ‘root‘@‘localhost‘ (using password:YES)解决方法
一、打开MySQL目录下的my.ini文件,在文件的[mysqld]下面添加一行 skip-grant-tables,保存并关闭文件;skip-grant-tables :跳过密码登录,登录时无需密码。my.ini :一般在和bin同目录下,如果没有的话可自己创…...
单目运算符、双目运算符、三目运算符
单目运算符是什么 单目运算符是指运算所需变量为一个的运算符 又叫一元运算符,其中有逻辑非运算符:!、按位取 反运算符:~、自增自减运算符:,-等。 逻辑非运算符【!】、按位取反运算符【~】、 自…...
离线数据仓库项目搭建——准备篇
文章目录(一)什么是数据仓库(二)数据仓库基础知识(三)数据仓库建模方式(1)星行模型(2)雪花模型(3)星型模型 VS 雪花模型(四…...
十七、本地方法接口的理解
什么是本地方法? 1.简单来讲,一个Ntive method 就是一个Java调用非Java代码的接口.一个Native Method 是这样一个Java方法:该方法的实现由非Java语言实现,比如C,这个特征并非Java所特有,很多其他的编程语言都由这一机制,比如在C中…...
【halcon】模板匹配参数之金字塔级数
背景 今天,在使用模板匹配的时候,突然程序卡死,CPU直接飙到100%。最后排查发现是模板匹配其中一个参数 NumLevels 导致的: NumLevels: The number of pyramid levels used during the search is determined with numLevels. If n…...
jupyter lab安装和配置
jupyter lab 安装和配置 一、jupyter lab安装并配置 安装jupyterlab pip install jupyterlab启动 Jupyter lab默认会打开实验环境的,也可以自己在浏览器地址栏输入127.0.0.1:8888/lab 汉化 pip install jupyterlab-language-pack-zh-CN刷新一下网页࿰…...
利用ngx_stream_return_module构建简易 TCP/UDP 响应网关
一、模块概述 ngx_stream_return_module 提供了一个极简的指令: return <value>;在收到客户端连接后,立即将 <value> 写回并关闭连接。<value> 支持内嵌文本和内置变量(如 $time_iso8601、$remote_addr 等)&a…...
云计算——弹性云计算器(ECS)
弹性云服务器:ECS 概述 云计算重构了ICT系统,云计算平台厂商推出使得厂家能够主要关注应用管理而非平台管理的云平台,包含如下主要概念。 ECS(Elastic Cloud Server):即弹性云服务器,是云计算…...
【入坑系列】TiDB 强制索引在不同库下不生效问题
文章目录 背景SQL 优化情况线上SQL运行情况分析怀疑1:执行计划绑定问题?尝试:SHOW WARNINGS 查看警告探索 TiDB 的 USE_INDEX 写法Hint 不生效问题排查解决参考背景 项目中使用 TiDB 数据库,并对 SQL 进行优化了,添加了强制索引。 UAT 环境已经生效,但 PROD 环境强制索…...
蓝牙 BLE 扫描面试题大全(2):进阶面试题与实战演练
前文覆盖了 BLE 扫描的基础概念与经典问题蓝牙 BLE 扫描面试题大全(1):从基础到实战的深度解析-CSDN博客,但实际面试中,企业更关注候选人对复杂场景的应对能力(如多设备并发扫描、低功耗与高发现率的平衡)和前沿技术的…...
汇编常见指令
汇编常见指令 一、数据传送指令 指令功能示例说明MOV数据传送MOV EAX, 10将立即数 10 送入 EAXMOV [EBX], EAX将 EAX 值存入 EBX 指向的内存LEA加载有效地址LEA EAX, [EBX4]将 EBX4 的地址存入 EAX(不访问内存)XCHG交换数据XCHG EAX, EBX交换 EAX 和 EB…...
Springboot社区养老保险系统小程序
一、前言 随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱,社区养老保险系统小程序被用户普遍使用,为方…...
SiFli 52把Imagie图片,Font字体资源放在指定位置,编译成指定img.bin和font.bin的问题
分区配置 (ptab.json) img 属性介绍: img 属性指定分区存放的 image 名称,指定的 image 名称必须是当前工程生成的 binary 。 如果 binary 有多个文件,则以 proj_name:binary_name 格式指定文件名, proj_name 为工程 名&…...
AI+无人机如何守护濒危物种?YOLOv8实现95%精准识别
【导读】 野生动物监测在理解和保护生态系统中发挥着至关重要的作用。然而,传统的野生动物观察方法往往耗时耗力、成本高昂且范围有限。无人机的出现为野生动物监测提供了有前景的替代方案,能够实现大范围覆盖并远程采集数据。尽管具备这些优势…...
如何在Windows本机安装Python并确保与Python.NET兼容
✅作者简介:2022年博客新星 第八。热爱国学的Java后端开发者,修心和技术同步精进。 🍎个人主页:Java Fans的博客 🍊个人信条:不迁怒,不贰过。小知识,大智慧。 💞当前专栏…...
DAY 45 超大力王爱学Python
来自超大力王的友情提示:在用tensordoard的时候一定一定要用绝对位置,例如:tensorboard --logdir"D:\代码\archive (1)\runs\cifar10_mlp_experiment_2" 不然读取不了数据 知识点回顾: tensorboard的发展历史和原理tens…...
