装饰模式(Decorator Pattern)详解


一、装饰模式简介

装饰模式(Decorator Pattern) 是一种 结构型设计模式,它允许你动态地给对象添加行为或职责,而无需修改其源代码,也不需要使用继承来扩展功能。

是一种用于替代继承的技术,它通过一种无须定义子类的方式给对象动态增加职责,使用对象之间的关联关系取代类之间的继承关系

引入了装饰类,在装饰类中既可以调用待装饰的原有类的方法,还可以增加新的方法,以扩展原有类的功能

你可以把它理解为“在不改变原对象的前提下,为其添加新功能的方式”。与继承不同的是,装饰模式是运行时动态组合的,更加灵活。

动态地给一个对象增加一些额外的职责。就扩展功能而言,装饰模式提供了一种比使用子类更加灵活的替代方案

以对客户透明的方式动态地给一个对象附加上更多的责任
可以在不需要创建更多子类的情况下,让对象的功能得以扩展

装饰模式的结构

装饰模式包含以下4个角色:

Component(抽象构件)
ConcreteComponent(具体构件)
Decorator(抽象装饰类)
ConcreteDecorator(具体装饰类)

在这里插入图片描述


二、解决的问题类型

装饰模式主要用于解决以下问题:

  • 当子类扩展不再适用:比如当你有多个可选功能,并且这些功能可以自由组合时,如果用继承方式实现,会导致类爆炸。
  • 希望以透明和动态的方式给对象添加职责:不需要修改客户端代码,就可以为对象添加新的功能。
  • 保持类责任清晰:每个装饰器只关注一个功能,符合单一职责原则。

三、使用场景

  1. 你想在不影响其他对象的情况下,动态、透明地给某个对象添加职责。
  2. 你需要在运行时决定是否添加某个功能,甚至多次添加多个功能。
  3. 替代多重继承的方案:避免由于多层继承导致的类爆炸问题。
  4. Java I/O 流系统中广泛使用装饰者模式,如 BufferedInputStream 包裹 FileInputStream
  5. 不能采用继承的方式对系统进行扩展或者采用继承不利于系统扩展和维护时可以使用装饰模式

四、实际生活案例

想象你在点一杯咖啡:

  • 基础咖啡:美式咖啡(Espresso)
  • 可选配料:牛奶(Milk)、摩卡(Mocha)、奶油(Whip)

每种配料都可以看作是对咖啡的“装饰”,你可以选择加一样,也可以叠加多种,最终价格也会随之变化。

这种“基础 + 动态添加”的场景非常适合使用装饰模式。


五、代码案例

我们通过一个简单的咖啡加料的例子来演示装饰模式。

1. 定义组件接口

// 组件接口:饮料
interface Beverage {double cost();     // 价格String description(); // 描述
}

2. 实现具体组件(基础对象)

// 具体组件:美式咖啡
class Espresso implements Beverage {@Overridepublic double cost() {return 2.0;}@Overridepublic String description() {return "Espresso";}
}

3. 抽象装饰器类(所有装饰器的基础)

// 抽象装饰器类
abstract class BeverageDecorator implements Beverage {protected Beverage decoratedBeverage;public BeverageDecorator(Beverage decoratedBeverage) {this.decoratedBeverage = decoratedBeverage;}@Overridepublic double cost() {return decoratedBeverage.cost();}@Overridepublic String description() {return decoratedBeverage.description();}
}

4. 具体装饰器类(装饰行为)

// 加牛奶的装饰器
class MilkDecorator extends BeverageDecorator {public MilkDecorator(Beverage decoratedBeverage) {super(decoratedBeverage);}@Overridepublic double cost() {return super.cost() + 0.5;}@Overridepublic String description() {return super.description() + ", Milk";}
}// 加摩卡的装饰器
class MochaDecorator extends BeverageDecorator {public MochaDecorator(Beverage decoratedBeverage) {super(decoratedBeverage);}@Overridepublic double cost() {return super.cost() + 0.7;}@Overridepublic String description() {return super.description() + ", Mocha";}
}// 加奶油的装饰器
class WhipDecorator extends BeverageDecorator {public WhipDecorator(Beverage decoratedBeverage) {super(decoratedBeverage);}@Overridepublic double cost() {return super.cost() + 0.6;}@Overridepublic String description() {return super.description() + ", Whip";}
}

5. 客户端调用示例

public class Client {public static void main(String[] args) {// 点一杯加牛奶和摩卡的咖啡Beverage beverage = new Espresso();beverage = new MilkDecorator(beverage);beverage = new MochaDecorator(beverage);System.out.println("Cost: $" + beverage.cost());System.out.println("Description: " + beverage.description());// 再加奶油beverage = new WhipDecorator(beverage);System.out.println("\nAfter adding whip:");System.out.println("Cost: $" + beverage.cost());System.out.println("Description: " + beverage.description());}
}

输出结果:

Cost: $3.2
Description: Espresso, Milk, MochaAfter adding whip:
Cost: $3.8
Description: Espresso, Milk, Mocha, Whip
抽象构件类典型代码(c++)
abstract class Component
{public abstract void Operation();
}
具体构件类典型代码
class ConcreteComponent : Component
{public override void Operation(){//基本功能实现}
}
抽象装饰类典型代码
class Decorator : Component
{
private Component component;  //维持一个对抽象构件对象的引用//注入一个抽象构件类型的对象public Decorator(Component component){this.component = component;}public override void Operation(){component.Operation();  //调用原有业务方法}
}
具体装饰类典型代码
class ConcreteDecorator : Decorator
{public ConcreteDecorator(Component component) : base(component){}public override void Operation() {base.Operation();  //调用原有业务方法AddedBehavior();  //调用新增业务方法}
//新增业务方法    
public void AddedBehavior() {	//功能扩展}
}
其他案例
  1. 某软件公司基于面向对象技术开发了一套图形界面构件库——VisualComponent,该构件库提供了大量基本构件,如窗体、文本框、列表框等,由于在使用该构件库时,用户经常要求定制一些特殊的显示效果,如带滚动条的窗体、带黑色边框的文本框、既带滚动条又带黑色边框的列表框等等,因此经常需要对该构件库进行扩展以增强其功能。
    现使用装饰模式来设计该图形界面构件库。
    在这里插入图片描述
  2. 变形金刚:变形金刚在变形之前是一辆汽车,它可以在陆地上移动。当它变成机器人之后除了能够在陆地上移动之外,还可以说话;如果需要,它还可以变成飞机,除了在陆地上移动还可以在天空中飞翔。
    在这里插入图片描述
  3. 多重加密系统:
    某系统提供了一个数据加密功能,可以对字符串进行加密。最简单的加密算法通过对字母进行移位来实现,同时还提供了稍复杂的逆向输出加密,还提供了更为高级的求模加密。用户先使用最简单的加密算法对字符串进行加密,如果觉得还不够可以对加密之后的结果使用其他加密算法进行二次加密,当然也可以进行第三次加密。现使用装饰模式设计该多重加密系统

在这里插入图片描述


六、优缺点分析

优点描述
比继承更灵活可在运行时动态添加功能,避免了类爆炸问题。可以通过一种动态的方式来扩展一个对象的功能,通过配置文件可以在运行时选择不同的具体装饰类,从而实现不同的行为
遵循开闭原则扩展功能时不需要修改原有代码。具体构件类与具体装饰类可以独立变化,用户可以根据需要增加新的具体构件类和具体装饰类,且原有类库代码无须改变,符合开闭原则
职责分离清晰每个装饰器只负责一个功能,便于维护和复用。可以对一个对象进行多次装饰
缺点描述
增加系统复杂度多个装饰器嵌套后可能让代码难以理解和调试。使用装饰模式进行系统设计时将产生很多小对象,大量小对象的产生势必会占用更多的系统资源,在一定程度上影响程序的性能
调试困难对象层层包装,追踪执行路径较为麻烦。比继承更加易于出错,排错也更困难,对于多次装饰的对象,调试时寻找错误可能需要逐级排查,较为烦琐
容易误用装饰顺序某些情况下装饰顺序会影响最终效果,需特别注意。

七、与其他模式对比(补充)

模式名称目标
代理模式控制对对象的访问,强调保护或增强,但不改变接口。
适配器模式解决接口不兼容问题,强调转换。
装饰模式动态增强对象功能,强调组合和扩展。

八、最终小结

装饰模式是一种非常实用的设计模式,尤其适合用于那些需要在不修改现有对象的前提下动态扩展其功能的场景。

它的核心思想是:

组合优于继承”,“透明地增强对象能力”。

在 Java 中,最经典的使用就是 IO 流体系,例如 BufferedReader(new InputStreamReader(System.in)),就是一个典型的装饰链。

作为一名 Java 开发工程师,掌握装饰模式可以帮助你写出更优雅、更灵活、更容易扩展的代码,特别是在开发插件化、模块化系统时尤为重要。

如果你正在设计一个需要支持“插件式”功能扩展的系统,或者希望避免因继承带来的类爆炸问题,那么装饰模式将是一个非常好的选择。


📌 一句话总结:

装饰模式就像“洋葱”,一层层包裹原始对象,在不破坏原有结构的前提下,动态赋予新功能。

九、扩展

透明装饰模式

透明(Transparent)装饰模式:要求客户端完全针对抽象编程,装饰模式的透明性要求客户端程序不应该将对象声明为具体构件类型或具体装饰类型,而应该全部声明为抽象构件类型。

对于客户端而言,具体构件对象和具体装饰对象没有任何区别。

可以让客户端透明地使用装饰之前的对象和装饰之后的对象,无须关心它们的区别
可以对一个已装饰过的对象进行多次装饰,得到更为复杂、功能更为强大的对象
无法在客户端单独调用新增方法AddedBehavior()

……
Component component_o,component_d1,component_d2; //全部使用抽象构件定义
component_o = new ConcreteComponent();
component_d1 = new ConcreteDecorator1(component_o);
component_d2 = new ConcreteDecorator2(component_d1);
component_d2.Operation();
//无法单独调用component_d2的AddedBehavior()方法
……

半透明装饰模式

半透明(Semi-transparent)装饰模式:用具体装饰类型来定义装饰之后的对象,而具体构件使用抽象构件类型来定义。
对于客户端而言,具体构件类型无须关心,是透明的;但是具体装饰类型必须指定,这是不透明的。

可以给系统带来更多的灵活性,设计相对简单,使用起来也非常方便
客户端使用具体装饰类型来定义装饰后的对象,因此可以单独调用AddedBehavior()方法
最大的缺点在于不能实现对同一个对象的多次装饰,而且客户端需要有区别地对待装饰之前的对象和装饰之后的对象

……
Component component_o; //使用抽象构件类型定义
component_o = new ConcreteComponent();
component_o.Operation();
ConcreteDecorator component_d; //使用具体装饰类型定义
component_d = new ConcreteDecorator(component_o);
component_d.Operation();
component_d.AddedBehavior(); //单独调用新增业务方法
……

以上内容部分由AI大模型生成,注意识别!

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

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

相关文章

NAT原理与实验指南:网络地址转换技术解析与实践

NAT实验 NAT(Network Address Translation,网络地址转换): NAT技术的介绍: 随着Internet用户的快速增长,以及地址分配不均等因素,IPv4地址(约40亿的空间地址)已经陷入不…

设计模式之【观察者模式】

目录 观察者模式中的角色 通过一个简单案例来演示观察者模式 被观察者接口 事件类型 up主类作为被观察者 观察者接口 粉丝类作为观察者 测试 测试结果 观察者模式中的角色 被观察者(observable)观察者(observer) 通过一个简单案例来演示观察者模式 被观察者接口 /*…

Linux sudo host权限提升漏洞(CVE-2025-32462)复现与原理分析

免责声明 本文所述漏洞复现方法仅供安全研究及授权测试使用; 任何个人/组织须在合法合规前提下实施,严禁用于非法目的; 作者不对任何滥用行为及后果负责,如发现新漏洞请及时联系厂商并遵循漏洞披露规则。 漏洞简述 Linux sudo是l…

【uni-ui】hbuilderx的uniapp 配置 -小程序左滑出现删除等功能

1.网址:https://ext.dcloud.net.cn/plugin?id181](https://ext.dcloud.net.cn/plugin?id181) 2.csdn讲解:https://blog.csdn.net/qq_40323256/article/details/114337128 3.uni-ui git:https://github.com/dcloudio/uni-ui 4.官方网址文档&…

记一次POST请求中URL中文参数乱码问题的解决方案

POST请求中URL中文参数乱码前言:一个常见的开发痛点一、问题现象与原因深度解析1. 典型问题场景2. 根本原因分析URL编码规范问题:编码解码过程不一致:IE浏览器特殊行为:二、前端解决方案1. 手动编码URL参数(推荐&#…

从存储热迁移流程了解 QEMU block layer

文章目录存储热迁移流程总体流程代码路径QEMU Block layer架构简述Block Job结构体设计状态转换Mirror block job拓扑结构构建过程数据结构存储热迁移流程 总体流程 Libvirt migrate 命令提供 copy-storage-all 选项支持存储热迁移,相应地,Libvirt 热迁…

【设计模式】命令模式 (动作(Action)模式或事务(Transaction)模式)宏命令

命令模式(Command Pattern)详解一、命令模式简介 命令模式(Command Pattern) 是一种 行为型设计模式(对象行为型模式),它将一个请求封装为一个对象,从而使你可以用不同的请求对客户进…

HTML5智能排班日历:动态排班一目了然

这个日历将具备以下功能: 显示一个标准的月度日历视图。可以自由切换上一个月和下一个月。在日历的每一天自动显示当天值班的人员。您可以很方便地在文件中修改值班人员列表和排班的起始日期。包括:动态生成日历网格处理月份切换根据排班规则计算并显示每天的值班人员<!DO…

深度剖析C++生态系统:一门老牌语言如何在开源浪潮中焕发新生?

&#x1f4dd;个人主页&#x1f339;&#xff1a;慌ZHANG-CSDN博客 &#x1f339;&#x1f339;期待您的关注 &#x1f339;&#x1f339; 一、前言&#xff1a;C的“长寿秘诀”是什么&#xff1f; C 诞生已超过 40 年。它经历了桌面应用、互联网爆发、移动时代&#xff0c;再…

60个功能OfficeBox 万彩办公大师:PDF 格式转换 OCR识别免费无广告

各位办公小能手们&#xff01;今天给大家介绍个超厉害的免费办公工具套装——OfficeBox万彩办公大师&#xff0c;是广州万彩科技整出来的。软件下载地址安装包 它里面有60多个没广告的绿色组件&#xff0c;简直像个百宝箱&#xff01;涵盖了PDF处理、格式转换、OCR识别、屏幕录…

拥抱主权AI:OpenCSG驱动智能体运营,共筑新加坡智能高地

2025年7月11日&#xff0c;由Linux基金会AI & Data、TikTok及LF Edge联合主办的 【LF AI & Data Day Singapore 2025】 在新加坡TikTok总部盛大启幕。本次大会以“Agent for SWE”为核心议题&#xff0c;汇聚全球顶尖AI开发者、企业领袖及开源社区先锋。作为国家主权AI…

单片机学习笔记.根据芯片数据手册写驱动程序(这里使用的是普中开发版,以DS1302为例)

硬件原理图部分&#xff1a; VCC2:是主电源 VCC1&#xff1a;是备用电源&#xff0c;此处没有使用VCC1 查芯片数据手册的网站&#xff1a; ALLDATASHEETCN.COM - 电子元件和半导体及其他半导体的数据表搜索网站。https://www.alldatasheetcn.com/ 1.由原理图可知对应引脚&…

Capture One24下载与保姆级安装教程!

软件下载 软件名称&#xff1a;Capture One24 软件语言&#xff1a;简体中文 软件大小&#xff1a;1.06G 系统要求&#xff1a;Windows7或更高&#xff0c;32/64位操作系统 硬件要求&#xff1a;CPU2.5GHz&#xff0c;RAM4G或更高 下载通道丨下载&#xff1a;https://too…

微信小程序(数据库)

const dbwx.cloud.database()//连接数据库db.collection("test").doc("b69f67c0626fac9000e123fc1ff07a42&#xff08;为要查询数据的id&#xff09;").get({success:res>{console.log(res)}})或getData(){db.collection("test").doc("&…

Apache CXF 漏洞曝光:存在拒绝服务与数据泄露双重风险

Apache软件基金会近日披露了一个影响多个Apache CXF版本的安全漏洞&#xff08;CVE-2025-48795&#xff09;。Apache CXF是开发者广泛使用的开源Web服务框架&#xff0c;用于构建基于SOAP和REST的应用程序。漏洞双重威胁该漏洞具有双重危害性&#xff1a;一方面可能通过内存耗尽…

Android 应用自动更新:从理论到实战的硬核指南

目录 1. 自动更新的核心逻辑:为什么它对用户体验至关重要? 自动更新的本质 为什么它如此重要? 2. 版本检测:如何优雅地发现“新大陆”? 设计版本检测的逻辑 实现版本检测的 API 请求 用户体验优化 3. 下载新版本:稳妥地获取安装包 下载的两种方式 注意事项 用户…

每日面试题05:ArrayList和LinkedList的底层原理

ArrayList与LinkedList深度解析&#xff1a;从底层原理到实战选择在Java的List接口实现中&#xff0c;ArrayList和LinkedList是最常用的两种选择。面试中“它们的区别”几乎是必问题&#xff0c;但仅仅停留在“数组vs链表”的层面显然不够。本文将从​​底层数据结构、内存布局…

python的慈善捐赠平台管理信息系统

前端开发框架:vue.js 数据库 mysql 版本不限 后端语言框架支持&#xff1a; 1 java(SSM/springboot)-idea/eclipse 2.NodejsVue.js -vscode 3.python(flask/django)–pycharm/vscode 4.php(thinkphp/laravel)-hbuilderx 数据库工具&#xff1a;Navicat/SQLyog等都可以 摘要 本文…

三十二、【核心功能改造】数据驱动:重构仪表盘与关键指标可视化

三十二、【核心功能改造】数据驱动:重构仪表盘与关键指标可视化 前言准备工作第一部分:后端实现 - 统计 API1. 创建 `DashboardStatsView`2. 注册统计 API 路由3. 后端初步测试第二部分:前端实现 - 重构仪表盘页面1. 创建 `api/dashboard.ts` API 服务2. 重构 `HomeView.vue…

神经网络与深度学习Python入门

一、神经网络基础 1. 神经元模型 在神经网络中&#xff0c;最基本的单元是神经元&#xff08;Neuron&#xff09;&#xff0c;也称为节点或单元。它模拟了生物神经系统中的神经元行为。一个典型的神经元模型包含多个输入&#xff08;x1,x2,…,xnx_1, x_2, \ldots, x_nx1​,x2​…