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.rhkb.cn/news/383221.html

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

相关文章

大语言模型-GPT2-Generative Pre-Training2

一、背景信息: GPT2是2019年由OpenAI 提出的预训练语言模型。 GPT2提出语言模型式无监督的多任务学习 。旨在通过无监督学习也能达到和finetune一样的效果,并且拥有更强的泛化能能力。 即提出利用语言模型做下游任务时,不需要下游任务的任何…

Python Flask入门到精通:详细教程和实战案例

前言 Flask是一个轻量级的Web框架,用于快速开发Web应用程序。它的设计理念是简洁、灵活和易于扩展,非常适合于从简单的单页应用到复杂的大型项目。通过Flask,可以创建各种Web应用程序,比如博客、电子商务网站、RESTful API等。 …

Unity3d打包到Android

本文参考: Unity3D新手教程:如何打包发布到Android_哔哩哔哩_bilibili 一、Unity 打包Android的环境搭建 1、工具安装 Unity Hub已经集成了Android的环境搭建。 选择Add modules 然后安装Android Build Support下的所有工具。 如果各个工具都安装成功…

H3CNE(路由基础、直连路由与静态路由)

目录 6.1 直连路由 6.2 静态路由理解性实验 6.2.1 配置直连路由 6.2.2 配置静态路由 6.3 路由表的参数与比较 6.3.1 优先级的比较 6.3.2 开销的比较 6.4 路由器中的等价路由、浮动路由、默认路由 6.4.1 等价路由 6.4.2 浮动路由 6.4.3 默认路由(缺省路由) 6.1 直连路…

银河麒麟服务器V10 SP3 安装人大金仓V009R001C001B0030

原文链接:银河麒麟服务器V10 SP3 安装人大金仓V009R001C001B0030 Hello,大家好啊,今天给大家带来一篇关于在银河麒麟服务器V10 SP3上安装人大金仓V009R001C001B0030的文章。人大金仓是国内知名的数据库管理系统,它在高性能、高可靠…

【MySQL进阶之路 | 高级篇】范式概述与第一范式

1. 范式简介 在关系型数据库中,关于数据表的设计的基本原则,规则就称为范式。可以理解为,一张数据表的设计结果需要满足的某种设计标准的级别。要想设计一个结构合理的关系型数据库,必须满足一定的范式。 范式的英文名是Normal …

C++树形结构(1 基础)

目录 一.基础: 1.概念: 2.定义: Ⅰ.树的相关基础术语: Ⅱ.树的层次: 3.树的性质: 二.存储思路: 1.结构体存储: 2.数组存储: 三.树的遍历模板: 四.信…

【SQL语句大全(MySQL)】

SQL语法 添加删除修改查询基本查询条件查询分组函数/聚合函数分组查询排序分页查询(限制查询)多表查询连接查询根据年代分类连接查询根据连接方式分类1、内连接2、左外连接3、右外连接 多张表连接的语法格式 嵌套查询 SQL语句书写顺序 添加 INSERT INTO…

深入浅出WebRTC—ULPFEC

FEC 通过在发送端添加额外的冗余信息,使接收端即使在部分数据包丢失的情况下也能恢复原始数据,从而减轻网络丢包的影响。在 WebRTC 中,FEC 主要有两种实现方式:ULPFEC 和 FlexFEC,FlexFEC 是 ULPFEC 的扩展和升级&…

贪心+背包

这道题比较坑的就是我们的对于相同截止时间的需要排个序&#xff0c;因为我们这个工作是有时间前后顺序的&#xff0c;我们如果不排序的话我们一些截止时间晚的工作就无法得到最优报酬 #include<bits/stdc.h> using namespace std;#define int long long int t; int n; c…

渗透测试——利用公网反弹shell到本地的两种方式,vmware虚拟机与主机的端口转发,本地ssh无法上线的问题解决

解决问题&#xff1a; 因长期使用本地模拟靶场&#xff0c;实战护网时并非模拟靶场&#xff0c;shell反弹需要利用公网测试。解决目标站无法反弹到本地的情况。解决本地是windows&#xff0c;虚拟机是kail、linux&#xff0c;无法相互转换流量的情况。 环境搭建 靶机 centOS7 …

线性代数|机器学习-P25线性规划和两人零和博弈

文章目录 0. 概述1. 线性规划问题1.1 定义1.2 举例 2. 线性规划中的对偶问题3. 最大流 - 最小割问题4. 两人零和博弈 MIT教授教学视频&#xff0c;讲得比较泛&#xff0c;需要另外学习很多知识补充 0. 概述 线性规划[LP]问题 线性规划是问题为线性求最值&#xff0c;约束也是求…

Spring Security认证授权介绍

一、目标 真正控制系统权限的&#xff0c;需要引入专门的安全框架才行&#xff0c;所以&#xff0c;我们今天重点来学习Spring家族中的一员Spring Security安全框架。最终呢&#xff0c;我们会使用Spring Security框架来控制养老项目的后台管理系统 能够熟悉常见的权限控制的方…

记录|C# winform布局学习

目录 前言一、自适应布局Step1. 添加AutoAdaptWindowsSize类Step2. Form中引用Step3. 创建SizeChanged事件函数Step4. 在Fram.Disiger中添加 更新时间 前言 参考视频&#xff1a; C#5分钟winform快速自适应布局 参考文章&#xff1a; 其他参考&#xff1a; 写这篇文章&#xff…

【C#】Visual Studio2022打包依赖第三方库的winForm程序为exe

0.简介 IDE&#xff1a;VS2022 平台&#xff1a;C# .NetFramework4.7.2 WinForm界面 有GDAL、EEplus第三方库的依赖&#xff0c;所以在其他未安装环境的电脑中功能无法使用。 1. 安装 1.1 运行文件输出 在VS扩展中选择管理扩展&#xff0c;安装&#xff1a;Microsoft Visua…

docker相关内容学习

一、docker的四部分 二、镜像相关命令 三、容器相关命令

【BUG】已解决:NameError: name ‘python‘ is not defined

NameError: name ‘python‘ is not defined 目录 NameError: name ‘python‘ is not defined 【常见模块错误】 【解决方案】 欢迎来到英杰社区https://bbs.csdn.net/topics/617804998 欢迎来到我的主页&#xff0c;我是博主英杰&#xff0c;211科班出身&#xff0c;就职于…

SVN文件夹没有图标(绿钩子和红感叹号)

3分钟教会你解决SVN文件夹没有绿勾和红色感叹号的问题_svn文件被改动过不显示红色-CSDN博客https://blog.csdn.net/weixin_43382915/article/details/124251563 关于SVN状态图标不显示的解决办法(史上最全) - 简书 (jianshu.com)https://www.jianshu.com/p/92e8e1f345c0

接入百度文心一言API教程

然后&#xff0c;编辑文章。点击AI识别摘要&#xff0c;然后保存即可 COREAIPOWER设置 暂时只支持经典编辑器.古腾堡编辑器等几个版本后支持.在比期间,你可以自己写点摘要 摘要内容 AL识别摘要 清空 若有收获&#xff0c;就点个赞吧 接入文心一言 现在百度文心一言&…

网站基本布局CSS

代码 <!DOCTYPE html> <html> <head><meta charset"utf-8"><meta name"viewport" content"widthdevice-width, initial-scale1"><title></title><style type"text/css">body {margi…