当前位置: 首页 > news >正文

Java 设计模式-装饰器模式

Java 设计模式-装饰器模式

模式概述

装饰器模式简介

定义:它允许向一个现有的对象添加新的功能,同时又不改变其结构。这种模式创建了一个装饰类,用来包装原有的类,并在保持类方法签名完整性的前提下,提供了额外的功能。

模式类型:结构型

作用

  • 动态地给一个对象添加一些额外的职责。就增加功能来说,装饰器模式相比生成子类更为灵活。
  • 可以在不修改原有代码的情况下,通过组合的方式扩展对象的功能,符合开闭原则(对扩展开放,对修改关闭)。
  • 提供了比继承更有弹性的替代方案,避免了因为子类继承导致的类爆炸问题。

典型应用场景

  • 在图形界面开发中,为组件添加滚动条、边框等装饰
  • Java 的 I/O 流体系中就大量使用了装饰器模式

我认为:装饰器将对象包装,在不改变类的情况下增加其功能,是一种相比继承更灵活的替代方案。

课程目标

  • 理解装饰器模式的核心思想和经典应用场景

  • 识别应用场景,使用装饰器模式解决灵活扩展对象的功能要求

  • 了解装饰器模式的优缺点


核心组件

角色-职责表

角色职责示例类名
抽象组件(Component)定义一个对象接口,可以给这些对象动态地添加职责。Coffee
具体组件(ConcreteComponent)实现了抽象组件的接口,是待装饰的对象。SimpleCoffee
抽象装饰器(Decorator)继承自抽象组件,并持有一个抽象组件的引用。CoffeeDecorator
具体装饰器(ConcreteDecorator)实现了抽象装饰器,负责给具体组件添加具体的职责。MilkDecorator, SugarDecorator

3.2 类图

«interface»
Component
+operation() : void
ConcreteComponent
+operation() : void
«abstract»
Decorator
-component : Component
+operation() : void
+setComponent(Component) : void
ConcreteDecoratorA
-addedState : string
+operation() : void
ConcreteDecoratorB
+addedBehavior() : void
+operation() : void

传统实现 VS 装饰者模式

传统实现(痛点版)

代码实现:

// 传统方式:通过继承扩展功能
public class SimpleCoffee {public double getCost() {return 2.0;}public String getDescription() {return "Simple Coffee";}
}// 加牛奶的咖啡
public class MilkCoffee extends SimpleCoffee {@Overridepublic double getCost() {return super.getCost() + 0.5;}@Overridepublic String getDescription() {return super.getDescription() + ", Milk";}
}// 加糖的咖啡
public class SugarCoffee extends SimpleCoffee {@Overridepublic double getCost() {return super.getCost() + 0.3;}@Overridepublic String getDescription() {return super.getDescription() + ", Sugar";}
}// 加牛奶和糖的咖啡 → 需要创建 MilkAndSugarCoffee,甚至更多组合
public class MilkAndSugarCoffee extends SimpleCoffee {@Overridepublic double getCost() {return super.getCost() + 0.5 + 0.3;}@Overridepublic String getDescription() {return super.getDescription() + ", Milk, Sugar";}
}

痛点总结

  • 类爆炸:每种功能组合都需要一个新类,N 个功能会产生 2^N 个子类。
  • 代码重复:每个子类都要重写 getCost()getDescription(),逻辑重复。
  • 不灵活:无法在运行时动态添加或移除功能。
  • 违反开闭原则:新增功能需要修改父类或增加大量子类。

装饰器模式(优雅版)

代码实现

// 1. 抽象组件
public interface Coffee {double getCost();String getDescription();
}// 2. 具体组件
public class SimpleCoffee implements Coffee {@Overridepublic double getCost() {return 2.0;}@Overridepublic String getDescription() {return "Simple Coffee";}
}// 3. 抽象装饰器
public abstract class CoffeeDecorator implements Coffee {protected Coffee coffee;public CoffeeDecorator(Coffee coffee) {this.coffee = coffee;}@Overridepublic double getCost() {return coffee.getCost();}@Overridepublic String getDescription() {return coffee.getDescription();}
}// 4. 具体装饰器
public class MilkDecorator extends CoffeeDecorator {public MilkDecorator(Coffee coffee) {super(coffee);}@Overridepublic double getCost() {return super.getCost() + 0.5;}@Overridepublic String getDescription() {return super.getDescription() + ", Milk";}
}public class SugarDecorator extends CoffeeDecorator {public SugarDecorator(Coffee coffee) {super(coffee);}@Overridepublic double getCost() {return super.getCost() + 0.3;}@Overridepublic String getDescription() {return super.getDescription() + ", Sugar";}
}// 使用示例
public class Main {public static void main(String[] args) {Coffee coffee = new SimpleCoffee();System.out.println(coffee.getDescription() + " $" + coffee.getCost());coffee = new MilkDecorator(coffee);System.out.println(coffee.getDescription() + " $" + coffee.getCost());coffee = new SugarDecorator(coffee);System.out.println(coffee.getDescription() + " $" + coffee.getCost());// 输出:Simple Coffee, Milk, Sugar $2.8}
}

优势

  • 运行时动态扩展:可以在程序运行时自由组合功能。
  • 符合开闭原则:新增功能只需添加新的装饰器类,无需修改已有代码。
  • 避免类爆炸:通过组合而非继承,有效减少子类数量。
  • 职责清晰:每个装饰器只关注一个功能点,符合单一职责原则。

局限

  • ⚠️ 装饰顺序影响结果:如日志装饰器和加密装饰器顺序不同,行为可能不同。
  • ⚠️ 过度使用导致复杂度上升:装饰链过长时,调试和理解成本增加。
  • ⚠️ 仅适用于支持接口/抽象基类的场景:被装饰对象必须有统一接口。

每次调用都是一次递归操作。


模式变体

  • 链式装饰器:通过责任链模式控制处理顺序
  • 优先级装饰器:支持按优先级排序的装饰器
  • 条件装饰器:根据业务规则动态决定是否应用装饰
  • 组合装饰器:将多个装饰器打包成复合装饰器

最佳实践

建议理由
保持单一职责每个装饰器只处理一个业务逻辑
使用工厂模式管理复杂装饰器组合的创建过程
记录装饰日志方便追踪处理流程和调试
限制装饰层级避免过度复杂的装饰链
提供默认实现确保基础功能的完整性

一句话总结

装饰器模式通过优雅的包装机制,实现了来一杯咖啡功能的动态组合与灵活扩展,完美解决了传统实现中的代码臃肿和扩展性难题。

如果关注Java设计模式内容,可以查阅作者的其他Java设计模式系列文章。😊

http://www.lryc.cn/news/620843.html

相关文章:

  • 线程P4 | 线程安全问题及解决方法
  • Linux信号产生
  • Linux下使用Samba 客户端访问 Samba 服务器的配置(Ubuntu Debian)
  • mysql 提示符及快捷执行
  • 从零开始搭建React+TypeScript+webpack开发环境——基于MobX的枚举数据缓存方案设计与实践
  • React 数据持久化:从 “刷新就丢“ 到 “永存不灭“ 的实现方案
  • WEBSTORM前端 —— 第4章:JavaScript —— 第3节:数据类型与类型转换
  • Streamlit实现Qwen对话机器人
  • Pytest自动化测试框架总结
  • 2025年机器视觉与信号处理国际会议(MVSP 2025)
  • springboot博客实战笔记02
  • 游戏行业DevOps实践:维塔士集团基于Atlassian工具与龙智服务构建全球化游戏开发协作平台
  • 阿里云RDS SQL Server实例之间数据库迁移方案
  • flstudio.exe安装教程|FL Studio怎么下载安装?超简单中文指南
  • K8S企业级应用与DaemonSet实战解析
  • 深入解析 HTTP 协议演进:从 1.0 到 3.0
  • 怎么判断晶振的好坏,有什么简单的办法
  • .NET 的 WebApi 项目必要可配置项都有哪些?
  • 【论文阅读-Part1】PIKE-RAG: sPecIalized KnowledgE and Rationale Augmented Generation
  • 机器学习算法篇(八)-------svm支持向量机
  • Android数据缓存目录context.getCacheDir与Environment.getExternalStorageDirectory
  • Linux 文件系统简介
  • 【大模型私有化部署】实战部分:Ollama 部署教程
  • 芯片 讲解| DP7272—24位、192kHz立体声音频编解码器
  • 百川开源大模型Baichuan-M2的医疗能力登顶第一?
  • Mybatis Plus 分页插件报错`GOLDILOCKS`
  • week1-[分支结构]中位数
  • imx6ull-驱动开发篇24——Linux 中断API函数
  • Docker 入门与实战:从环境搭建到项目部署
  • Windows批处理脚本自动合并当前目录下由You-get下载的未合并的音视频文件