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是抽象装饰器,MilkWhip是具体装饰器。客户端可以通过装饰器来动态地添加调料和装饰。

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)

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

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

相关文章

特征工程方法总结

方法有以下这些 首先看数据有没有重复值、缺失值情况 离散:独热 连续变量:离散化(也成为分箱) 作用:1.消除异常值影响 2.引入非线性因素,提升模型表现能力 3.缺点是会损失一些信息 怎么分:…

爬取百度图片,想爬谁就爬谁

前言 既然是做爬虫,那么肯定就会有一些小心思,比如去获取一些自己喜欢的资料等。 去百度图片去抓取图片吧 打开百度图片网站,点击搜索xxx,打开后,滚动滚动条,发现滚动条越来越小,说明图片加载…

3D 渲染一个房屋需要多长时间?

3D 渲染一个房屋总共需要 6-10 个工作日,主要取决于项目的复杂性和最终 3D 渲染的质量,图像越逼真,效果图渲染所需时间就越长。 1.3D建模 创建 3D 模型是第一步,所需时间可能因项目的复杂程度而有很大差异。一个简单的住宅渲染可…

D3.高精度

1.分类情况 AB、A-B、A*a、A/b A和B指的是超大超长整数&#xff0c;长度<1e6; a的值<10000&#xff1b; 2.大整数的存储 int 变量肯定是存不了这么大的数的&#xff0c;做法是将大整数先存到string字符串&#xff0c;再使用字符串的访问方式&#xff0c;将每一位数存到…

单机游戏分享:波与月夜之青莲单机游戏下载,2D和风动作游戏

在《波与月夜之青莲》中穿越一个充满神话和传奇的神秘世界。这是款丰富多彩的手绘冒险游戏&#xff0c;灵感来自于日本的民间传说。 扮演波&#xff0c;一朵从天而降的天体花&#xff0c;在一道古老而神秘的仪式中扮演关键的角色&#xff0c;展开一段神秘的旅程。使用你的传说…

一、C#概述

本文是网页版《C# 12.0 本质论》第一章解读。欲完整跟踪本系列文章&#xff0c;请关注并订阅我的Essential C# 12.0解读专栏。 前言 第一章的内容非常简单&#xff0c;毕竟仅仅是Introducing C#。不过正如《0.前言》所述&#xff0c;《C# 12.0本质论》本身就不是一本零基础的…

rv1126利用rkmedia、opencv、rockx……完成人脸识别

一、总体框架 视频采集、处理使用rkmedia&#xff1a;vi模块进行视频输入、rga模块进行视频处理 人脸识别&#xff1a;先获取rga输出码流&#xff0c;再调用rkmedia的模型对人脸进行推理&#xff08;线程1&#xff09; 打框框&#xff1a;opencv&#xff08;线程2&#xff0…

Golang | Leetcode Golang题解之第264题丑数II

题目&#xff1a; 题解&#xff1a; func nthUglyNumber(n int) int {dp : make([]int, n1)dp[1] 1p2, p3, p5 : 1, 1, 1for i : 2; i < n; i {x2, x3, x5 : dp[p2]*2, dp[p3]*3, dp[p5]*5dp[i] min(min(x2, x3), x5)if dp[i] x2 {p2}if dp[i] x3 {p3}if dp[i] x5 {p5…

艾迈斯欧司朗与greenteg推出的突破性体温监测技术已成为全球铁人三项的关键技术支持

中国 上海&#xff0c;2024年7月22日——全球领先的光学解决方案供应商艾迈斯欧司朗&#xff08;瑞士证券交易所股票代码&#xff1a;AMS&#xff09;今日宣布&#xff0c;与知名合作伙伴greenteg携手推出的CORE传感器为耐力运动领域带来新变革——其体温监测技术已成为全球铁人…

分类预测 | Matlab实现BES-LSSVM秃鹰算法优化最小二乘支持向量机多特征分类预测/故障诊断

分类预测 | Matlab实现BES-LSSVM秃鹰算法优化最小二乘支持向量机多特征分类预测/故障诊断 目录 分类预测 | Matlab实现BES-LSSVM秃鹰算法优化最小二乘支持向量机多特征分类预测/故障诊断分类效果基本介绍程序设计参考资料 分类效果 基本介绍 Matlab实现BES-LSSVM秃鹰算法优化最…

使用wireshark第一次捕获数据包

打开wireshark&#xff1a; 点击捕获&#xff0c;选项。 这里我选择以太网&#xff0c;然后点开始&#xff1a; 然后就成这样了&#xff1a; 点击左上角那个红色的按钮&#xff0c;可以暂停捕获&#xff0c;就变成了下面的样子&#xff1a; 这三个框有自己的名字&…

721.力扣每日一题7/15 Java(并查集)

博客主页&#xff1a;音符犹如代码系列专栏&#xff1a;算法练习关注博主&#xff0c;后期持续更新系列文章如果有错误感谢请大家批评指出&#xff0c;及时修改感谢大家点赞&#x1f44d;收藏⭐评论✍ 目录 解题思路 解题过程 时间复杂度 空间复杂度 Code 解题思路 本题的…

有什么图片转word 免费软件?6个软件帮助你快速转换文件

有什么图片转word 免费软件&#xff1f;6个软件帮助你快速转换文件 转换图片到Word文档的免费软件通常包括一些在线工具和桌面应用程序&#xff0c;它们可以帮助用户将图片中的文本提取并转换为可编辑的Word格式。以下是六个推荐的免费软件&#xff1a; 迅捷文字识别&#xff…

【深度学习教程】

文章目录 pytorch官方教程知识蒸馏&#xff1a;https://pytorch.org/tutorials/beginner/knowledge_distillation_tutorial.html 李宏毅-机器学习/深度学习https://speech.ee.ntu.edu.tw/~hylee/ml/2021-spring.phphttps://speech.ee.ntu.edu.tw/~hylee/ml/2022-spring.phphttp…

【LeetCode】day18:530 - 二叉搜索树的最小绝对差, 501 - 二叉搜索树中的众数, 236 - 二叉树的最近公共祖先

LeetCode 代码随想录跟练 Day18 530.二叉搜索树的最小绝对差501.二叉搜索树中的众数236.二叉树的最近公共祖先 530.二叉搜索树的最小绝对差 题目描述&#xff1a; 给你一个二叉搜索树的根节点 root &#xff0c;返回 树中任意两不同节点值之间的最小差值 。 差值是一个正数&am…

基于组态王和S7-200实现传感器连接的简单人机界面

在当今社会&#xff0c;工业自动化正以前所未有的速度发展&#xff0c;成为推动经济增长和提升生产效率的关键因素之一。随着科技的不断进步&#xff0c;自动化不仅仅是制造业的专利&#xff0c;也逐渐渗透到农业、交通、建筑等各个领域&#xff0c;为人们的生活带来了诸多变革…

星际漫游闪耀LEC授权展,COSMOSPANDA星际熊猫与BUNBUNX包小冥共绘国潮新篇章

上海&#xff0c;2024年7月17日 —— 在全球瞩目的2024年LEC全球授权展上海站盛大开幕之际&#xff0c;星际漫游携其两大国潮明星IP——COSMOSPANDA星际熊猫与bunbunx包小冥精彩亮相&#xff0c;为现场观众带来了一场融合视觉与文化的国潮盛宴。 展位盛况空前&#xff0c;国潮…

uniapp封装请求拦截器,封装请求拦截和响应拦截的方法

首先我们先看一下uni官方给开发者提供的uni.request用来网络请求的api 1 2 3 4 5 6 7 8 9 uni.request({ url: , method: GET, data: {}, header: {}, success: res > {}, fail: () > {}, complete: () > {} }); 可以看到我们每次请求数据的时候都需…

pytest-fixture

资料来源&#xff1a;虫师2020的个人空间-虫师2020个人主页-哔哩哔哩视频 支持类似unittest风格的fixture&#xff0c;即setup和teardown class类中的方法分类 类方法可以直接调用&#xff0c;需要添加装饰器&#xff0c;修改类中的变量 实例方法&#xff0c;需要先实例化&…

【移动应用开发】创建Hello World应用

目录 一、安装Android Studio &#xff08;2023.1.28&#xff09; 二、创建HelloWorld的项目 1. 新建一个项目&#xff0c;选择Empty Views Activity 2. 点击next之后&#xff0c;为项目命名为HelloWorld 3. 点击Finish之后等待项目创建 三、观察项目结构 1. 选择以Proj…