《程序猿之设计模式实战 · 装饰者模式》

📢 大家好,我是 【战神刘玉栋】,有10多年的研发经验,致力于前后端技术栈的知识沉淀和传播。 💗

🌻 CSDN入驻不久,希望大家多多支持,后续会继续提升文章质量,绝不滥竽充数,欢迎多多交流。👍

文章目录

      • 写在前面的话
      • 基础介绍
      • 代码实现
      • Spring 使用装饰者模式
      • 动态代理VS装饰者模式
      • 装饰者模式的简化
      • 总结陈词

写在前面的话

上一篇文章《程序猿之设计模式实战 · 策略模式》介绍的了策略模式的实际运用,这篇紧随其后,补充上装饰者模式。

装饰者模式也是相当实用的,适合很多场景,且听慢慢道来。


基础介绍

基础概念:

装饰者模式是一种结构型设计模式,它允许在不改变对象自身的情况下,动态地给对象添加新的功能。通过将功能封装在装饰类中,装饰者模式提供了一种灵活的方式来扩展对象的行为。

装饰模式以对客户透明的方式动态地给一个对象附加上更多的责任。换言之,客户端并不会觉得对象在装饰前和装饰后有什么不同。装饰模式可以在不使用创造更多子类的情况下,将对象的功能加以扩展。

类图:

组成部分:

装饰者模式通常由以下几个部分组成:

1、组件接口(Component):定义一个接口,声明具体组件和装饰者都需要实现的方法。

2、具体组件(ConcreteComponent):定义一个将要接收附加责任的类,实现组件接口,表示被装饰的对象。

3、装饰者(Decorator):也是实现组件接口的类,持有一个组件对象的引用,并在其方法中调用该组件的方法。装饰者可以在调用前后添加额外的功能。

4、具体装饰者(ConcreteDecorator):继承自装饰者类,具体实现添加的功能。

常用场景:

装饰者模式适用于以下场景:

1、需要动态添加功能:当你希望在运行时为对象添加功能,而不影响其他对象时。

2、避免子类爆炸:当功能组合的数量较多时,使用装饰者模式可以避免创建大量的子类。

3、增强类的功能:当你希望在不修改现有类的情况下,增强类的功能。


代码实现

示例:以文本输出,添加粗体、斜体的职责为例。

Step1、定义组件接口

public interface Text {String getContent();
}

Step2、定义具体组件

public class BaseText implements Text {private final String content;public BaseText(String content) {this.content = content;}@Overridepublic String getContent() {return content;}
}

Step3、定义装饰者

public abstract class TextDecorator implements Text {protected Text text;public TextDecorator(Text text) {this.text = text;}
}

Step4、定义具体装饰者

public class BoldTextDecorator extends TextDecorator {public BoldTextDecorator(Text text) {super(text);}@Overridepublic String getContent() {return "<b>" + text.getContent() + "</b>";}
}public class ItalicTextDecorator extends TextDecorator {public ItalicTextDecorator(Text text1) {super(text1);}@Overridepublic String getContent() {return "<i>" + text.getContent() + "</i>";}
}

Step5、客户端测试

public class DecoratorClient {public static void main(String[] args) {Text plainText = new BaseText("Hello, 战神!");// 添加粗体装饰Text boldTextDecorator = new BoldTextDecorator(plainText);System.out.println(boldTextDecorator.getContent());// 输出内容:// <b>Hello, 战神!</b>// 添加斜体装饰Text italicTextDecorator = new ItalicTextDecorator(boldTextDecorator);System.out.println(italicTextDecorator.getContent());// 输出内容:// <i><b>Hello, 战神!</b></i>}
}

点评一下:

从代码看,还是挺清晰的,各司其职。

可以理解为不影响原有功能,动态进行功能的增强就是装饰者模式。

特别适合解决例如购物、点餐、SKU等组合的情况,即容易出现“类爆炸”的场合。


Spring 使用装饰者模式

在 Spring 框架中,装饰者模式被广泛应用于以下几个方面:

1、AOP(面向切面编程):

Spring AOP 使用代理模式来实现切面功能,实际上可以看作是对目标对象的装饰。通过在目标对象周围添加切面逻辑(如事务管理、日志记录等),而不改变目标对象的代码。

2、Spring MVC 的 HandlerInterceptor:

在 Spring MVC 中,HandlerInterceptor 可以被视为一种装饰者模式的实现。它允许在请求处理的不同阶段(如请求前、请求后)添加额外的处理逻辑,而不需要修改控制器本身的代码。

3、BeanPostProcessor:

Spring 的 BeanPostProcessor 接口允许在 Spring 容器创建和初始化 bean 之后,添加额外的功能或修改 bean 的属性。这种机制也可以看作是对 bean 的装饰。

总结:装饰者模式是一种灵活的设计模式,允许在运行时动态地为对象添加功能。在 Spring 框架中,装饰者模式的思想被广泛应用于 AOP、拦截器和 bean 处理等多个方面,使得功能的扩展变得更加灵活和可维护。


动态代理VS装饰者模式

相似之处:

细心的伙伴可能观察到,日常接触的动态代理,是否装饰者模式虽然有相似之处。

1、从增强功能上看,两者都可以在不修改原有类的情况下,为对象添加额外的功能或行为。

2、从代码结构上看:都涉及到对原有对象的封装,通常通过组合或代理的方式来实现。

但其实两者还是存在区别:

1、实现方式:

装饰者模式:通常通过创建一个装饰者类来包装原有对象,装饰者类实现与原有对象相同的接口,并在其方法中调用原有对象的方法,同时添加新的行为。

动态代理:使用反射机制在运行时创建一个代理对象,该代理对象可以在方法调用时添加额外的逻辑。动态代理不需要创建具体的装饰类,而是通过代理类来增强功能。

2、使用场景:

装饰者模式:适用于需要在多个地方以不同方式增强对象的场景,通常是为了增加对象的功能。

动态代理:适用于需要在运行时决定增强逻辑的场景,比如 AOP(面向切面编程)中的横切关注点。

3、灵活性:

装饰者模式:需要在编译时确定装饰的组合,通常是静态的。

动态代理:可以在运行时动态决定增强的逻辑,具有更高的灵活性。

总结一下:

动态代理可以被视为一种特殊的装饰者模式实现,但它更侧重于运行时的动态性和灵活性,而装饰者模式则更关注于通过组合来增强对象的功能。因此,虽然它们有相似的目的,但在实现和使用上有明显的区别。


装饰者模式的简化

实际开发中,往往场景不会都是这么复杂(没这么多元素),此时可以考虑简化。

Tips:实战要灵活,学会变通,而不要一味追求标准、套用标准。

如果只有一个待装饰的类ConcreteComponent,那么可以考虑去掉抽象的 Component 类(接口),把Decorator作为一个ConcreteComponent子类。

如果只有一个ConcreteDecorator类,那么就没有必要建立一个单独的Decorator类,而可以把Decorator和ConcreteDecorator的责任合并成一个类。

是不是很简洁,不过怎么越看越像JDK动态代理?

万变不如其宗,根源应该靠面向接口编程以及持有被代理/装饰的对象吧。


总结陈词

还是那句话,不用过多的纠结在用的是哪个设计模式,实现的是什么标准。

遇到实际问题能使用合适的方式解决,同时代码经得起推敲和扩展,才是最主要的。

回到装饰者模式,常用于解决“类爆炸”的问题,动态附加功能,同时基本满足面向对象开发原则的全部原则。

不过,装饰模式也有相应的缺点,由于使用装饰模式,可以比使用继承关系需要较少数目的类。使用较少的类,当然使设计比较易于进行。但是,在另一方面,使用装饰模式会产生比使用继承关系更多的对象。更多的对象会使得查错变得困难,特别是这些对象看上去都很相像。

当然,装饰者还是很强的,遇到适合的场景果断用上,缺点可以忽略不计。

还是那句话,你可以不用,但不能不会。

💗 后续会逐步分享企业实际开发中的实战经验,有需要交流的可以联系博主。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/423179.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

欢迎来到我的Java世界“抽象类”

前言 在上篇中我们学习到了继承的概念、语法等等&#xff0c;那么小编将来为大家方享下一篇Java中的抽象类。 1.抽象类的概念 2.抽象类的语法 3.抽象类的特性 4.抽象类的作用 一&#xff1a;讲到抽象类&#xff0c;大家是不是会很迷惑什么是抽象类&#xff1f; 在面向对象…

安卓framework美化手势导航侧滑返回UI

文章目录 手势导航的侧滑返回效果图原生效果如下:要实现的功能,: 实现代码1. 初始化代码2. 修改绘制的代码,进行箭头与退出UI的显示3. 拖动的时候手指上下移动时,箭头ui跟着移动 以下是一些其他可以美化安卓右滑手势拖动 UI 的方法&#xff1a;视觉效果方面形状和布局方面 安卓…

加密软件有哪些数据防护功能?

1.文件透明加密&#xff1a;采用透明加密技术&#xff0c;自动对指定类型的敏感文件进行实时加密&#xff0c;确保数据在存储和传输过程中的安全性。 2.权限管理与访问控制&#xff1a;通过细粒度的权限管理&#xff0c;控制员工对敏感数据的访问权限&#xff0c;包括读取、修…

PHP一键约课高效健身智能健身管理系统小程序源码

一键约课&#xff0c;高效健身 —— 智能健身管理系统让健康触手可及 &#x1f3cb;️‍♀️ 告别繁琐&#xff0c;一键开启健身之旅 你还在为每次去健身房前的繁琐预约流程而烦恼吗&#xff1f;现在有了“一键约课高效健身智能健身管理系统”&#xff0c;所有问题都迎刃而解…

Solana核心漏洞技术详解

8月9日&#xff0c;Solana团队齐心协力解决了一个严重的安全漏洞。这次秘密修复详情可以在GitHub上查询到。CertiK团队对这一漏洞进行了深入分析。 1. Solana漏洞起因 8月9日&#xff0c;Solana验证者和客户端团队齐心协力解决了一个严重的安全漏洞。Solana验证者Laine表示&am…

TypeScript 扩展

扩展 ?:可选参数 可选链事实上并不是TypeScript独有的特性&#xff0c;它是ES11&#xff08;ES2020&#xff09;中增加的特性 可选链使用可选链操作符 ? 作用是当对象的属性不存在时&#xff0c;会短路&#xff0c;直接返回undefined&#xff0c;如果存在&#xff0c;那么…

【机器学习】从零开始理解深度学习——揭开神经网络的神秘面纱

1. 引言 随着技术的飞速发展,人工智能(AI)已从学术研究的实验室走向现实应用的舞台,成为推动现代社会变革的核心动力之一。而在这一进程中,深度学习(Deep Learning)因其在大规模数据处理和复杂问题求解中的卓越表现,迅速崛起为人工智能的最前沿技术。深度学习的核心是…

安卓玩机工具-----ADB方式的刷机玩机工具“秋之盒”’ 测试各项功能预览

秋之盒 安卓玩机工具-秋之盒是一款ADB刷机工具箱&#xff0c;基于谷歌ADB的一款绿色安装&#xff0c;具备了海量扩展模块,支持ADB刷机救砖、一键激活黑域、adb指令修复等功能&#xff0c;是一款开源、免费、易用的手机刷机工具&#xff01; 并且是一款开源、免费、易用的图形化…

细致刨析JDBC ① 基础篇

目录 一、JDBC概述 1.JDBC的概念 ​编辑2.JDBC的核心组成 ① 接口规范: ② 实现规范: 二、JDBC快速入门 1.JDBC搭建步骤 三、核心API理解 1.注册驱动 2.Connection 3.Statement 4.PreparedStatement 5.ResultSet 四、基于Preparedment实现CRUD 1.查询单行单列 2.查询单行…

P3565 [POI2014] HOT-Hotels

~~~~~ P3565 [POI2014] HOT-Hotels ~~~~~ 总题单链接 ~~~~~ 2024.9.10&#xff1a;DP方程有问题&#xff0c;已修改&#xff0c;同时更新了长链剖分优化版本。 思路 ~~~~~ 设 g [ u ] [ i ] g[u][i] g[u][i] 表示在 u u u 的子树内&#xff0c;距离 u u u 为 i i i 的点的…

管家婆云辉煌手机端怎么连接蓝牙打印机?

管家婆云辉煌手机端可以连接蓝牙打印机&#xff0c;这样手机可以发送打印任务到蓝牙打印机&#xff0c;完成打印任务。具体的设置步骤如下&#xff1a; 一、首先完成手机和蓝牙打印机配对&#xff0c;打开蓝牙打印机后。手机开启蓝牙和定位服务 点击手机设置&#xff0c;进入手…

jmeter压力测试,通过LLM利用RAG实现知识库问答,NEO4J部署,GraphRAG以知识图谱在查询时增强提示实现更准确的知识库问答(9/7)

前言 这周也是杂七杂八的一天&#xff08;高情商&#xff1a;我是一块砖&#xff0c;哪里需要往哪里搬&#xff09;&#xff0c;首先是接触了jemter这个压力测试工具&#xff0c;然后帮公司的AIGC项目编写使用手册和问答手册的第一版&#xff0c;并通过这个平台的智能体实现知识…

【数据结构】排序算法系列——希尔排序(附源码+图解)

希尔排序 算法思想 希尔排序&#xff08;Shell Sort&#xff09;是一种改进的插入排序算法&#xff0c;希尔排序的创造者Donald Shell想出了这个极具创造力的改进。其时间复杂度取决于步长序列&#xff08;gap&#xff09;的选择。我们在插入排序中&#xff0c;会发现是对整体…

探索数据可视化的奥秘:Seaborn库的魔力

文章目录 探索数据可视化的奥秘&#xff1a;Seaborn库的魔力背景&#xff1a;为何选择Seaborn&#xff1f;Seaborn是什么&#xff1f;如何安装Seaborn&#xff1f;简单函数介绍与示例场景应用示例常见问题与解决方案总结 探索数据可视化的奥秘&#xff1a;Seaborn库的魔力 背景…

xLSTM模型学习笔记

笔记来源&#xff1a;bilibili LSTM 回顾 原始的 LSTM 是为了解决 RNN 时序反向传播中梯度消失和爆炸问题而提出的。 其所谓的门控机制&#xff0c;其实就是一种时序上的注意力机制&#xff0c;相当于把不同时间进行"掺和"&#xff0c;是对时序信息的一种选择性控制…

【ARM compiler】生成ELF文件中包含了那些内容

【更多软件使用问题请点击亿道电子官方网站】 文档目标&#xff1a;用于了解ARM compiler生成的ELF文件中存储的内容进行了解 问题场景&#xff1a;ELF文件主要用于通过调试软件对于代码的运行顺序和数据链接等内容进行分析。了解一下ARM compiler生成ELF文件包含那些内容。 软…

Linux find案例

目录 1. 只查找当前目录&#xff0c;不查找子目录中的指定文件2. 查找到的文件批量复制到指定文件夹中3. 查找到的文件批量修改所属用户和组4. 查找到的文件批量添加执行权限5. 查找到的文件内容全部导入指定文件6. 查找指定目录下指定用户所属的文件7. 获取当前目录下&#xf…

[Redis] Redis中的String类型

&#x1f338;个人主页:https://blog.csdn.net/2301_80050796?spm1000.2115.3001.5343 &#x1f3f5;️热门专栏: &#x1f9ca; Java基本语法(97平均质量分)https://blog.csdn.net/2301_80050796/category_12615970.html?spm1001.2014.3001.5482 &#x1f355; Collection与…

电脑开机速度慢怎么解决?

电脑开机速度慢怎么解决&#xff1f;电脑开机速度慢的原因可以是多方面的&#xff0c;以下是一些常见的原因&#xff1a; 启动项过多&#xff1a; 许多软件在系统启动时会自动启动&#xff0c;导致启动项过多&#xff0c;从而延长了开机时间。过时的驱动程序&#xff1a; 设备…

《基于深度半监督学习的目标检测综述》泛读

基于深度半监督学习的目标检测方法分为 1、生成式方法 2、一致性正则化方法 3、基于图的方法 4、伪标记方法和混合方法 然后基于常用数据集 对典型方法进行了性能对比&#xff0c;最后分析了其挑战和发展趋势&#xff0c;旨在为相关研究提供参考 收获就是&#xff1a; 1…