Java 23 种设计模式:从踩坑到精通 | 番外:装饰器模式 —— 物流包裹增值服务实战
Java 23 种设计模式从踩坑到精通 | 番外装饰器模式 —— 物流包裹增值服务实战摘要装饰器模式通过“包装”的方式在不改变原有类的情况下动态地为对象添加功能支持多层嵌套和运行时组合是“组合优于继承”的经典范例。本文结合智能物流中“包裹增值服务保价/加急/易碎品包装/货到付款”的场景完整展示如何用装饰器模式灵活组合多种增值服务避免 5 种服务产生 31 个继承子类的爆炸问题并与外观模式深度对比帮你掌握“动态增强”的设计精髓。️本文阅读地图3 分钟速览为什么 5 种增值服务用继承会产生 31 个子类✅ 装饰器模式核心角色抽象构件、具体构件、抽象装饰器、具体装饰器手写包裹增值服务保价、加急、易碎品包装、货到付款任意组合 装饰器 vs 外观做加法 vs 做减法 面试必问“装饰器模式和代理模式有什么区别”《Java 23 种设计模式从踩坑到精通》开篇系列介绍与目录 正篇装饰器模式 Decorator —— 比继承更灵活的扩展方式 当前番外 · 装饰器模式 × 物流包裹增值服务 返回系列总目录1. 物流增值服务的组合爆炸问题在智能物流系统中一个基础包裹可以叠加多种增值服务保价、加急、易碎品包装、货到付款…… 每种服务都会影响最终的描述和费用。如果为每种组合创建一个子类仅仅 5 种服务的组合就会产生 31 个子类。装饰器模式的解决思路用一系列包装器对象去“包裹”核心对象每个包装器在核心行为前后添加自己的功能再将调用转发给被包装对象。就像给包裹一层层加服务——先包装易碎品再保价再加急——每一层只增加自己的费用和描述。1.1 你的场景该不该用装饰器判断标准是 → 用装饰器否 → 用其他方式需要动态、透明地给对象添加功能✅❌功能可以任意组合且组合顺序可能影响结果✅❌不想用继承导致子类爆炸✅❌功能增强是固定的且不会动态变化❌直接修改类或使用继承只是想给对象加日志接口本身没变❌用 Lambda 函数式组合更轻量2. 装饰器模式 UML物流包裹增值服务场景3. 完整源码实现3.1 抽象构件物流包裹接口/** * 抽象构件物流包裹 * 定义包裹的基本行为接口 */publicinterfaceLogisticsPackage{StringgetDescription();doublegetCost();}白话所有包裹——无论是基础包裹还是加过保价、加急的包裹——都必须能“报描述”和“算费用”。3.2 具体构件基础包裹/** * 具体构件基础包裹 * 最基础的包裹实现提供默认描述和基础运费 */publicclassBasicPackageimplementsLogisticsPackage{privateStringdescription;privatedoublebaseCost;publicBasicPackage(Stringdescription,doublebaseCost){this.descriptiondescription;this.baseCostbaseCost;}OverridepublicStringgetDescription(){returndescription;}OverridepublicdoublegetCost(){returnbaseCost;}}白话这就是最原始的包裹——只有描述和基础运费。所有增值服务都是在这层基础上“包”上去的。3.3 抽象装饰器/** * 抽象装饰器包裹装饰器 * 持有 LogisticsPackage 引用实现与构件相同的接口 */publicabstractclassPackageDecoratorimplementsLogisticsPackage{protectedLogisticsPackagepkg;publicPackageDecorator(LogisticsPackagepkg){this.pkgpkg;}OverridepublicStringgetDescription(){returnpkg.getDescription();// 默认委托给被装饰对象}OverridepublicdoublegetCost(){returnpkg.getCost();// 默认委托给被装饰对象}}白话抽象装饰器同时实现了LogisticsPackage接口并持有一个LogisticsPackage引用——既是包裹又包装了另一个包裹。这就是装饰器能层层嵌套的关键。3.4 具体装饰器A保价服务/** * 具体装饰器A保价服务 * 按声明价值的百分比收取保价费 */publicclassInsuranceDecoratorextendsPackageDecorator{privatedoubledeclaredValue;privatestaticfinaldoubleINSURANCE_RATE0.03;publicInsuranceDecorator(LogisticsPackagepkg,doubledeclaredValue){super(pkg);this.declaredValuedeclaredValue;}OverridepublicStringgetDescription(){returnpkg.getDescription() 【保价服务】(声明价值: ¥declaredValue);}OverridepublicdoublegetCost(){returnpkg.getCost()declaredValue*INSURANCE_RATE;}}白话保价装饰器在被包装包裹的描述上追加保价信息费用上追加保价费。它不关心被包装的是基础包裹还是已经加过急的包裹——一视同仁。3.5 具体装饰器B加急服务/** * 具体装饰器B加急服务 */publicclassExpressDecoratorextendsPackageDecorator{privateExpressTypeexpressType;publicenumExpressType{NEXT_DAY(15.0),SAME_DAY(30.0);privatefinaldoublefee;ExpressType(doublefee){this.feefee;}publicdoublegetFee(){returnfee;}}publicExpressDecorator(LogisticsPackagepkg,ExpressTypeexpressType){super(pkg);this.expressTypeexpressType;}OverridepublicStringgetDescription(){returnpkg.getDescription() 【加急服务】(expressType);}OverridepublicdoublegetCost(){returnpkg.getCost()expressType.getFee();}}3.6 具体装饰器C易碎品包装/** * 具体装饰器C易碎品包装 */publicclassFragileDecoratorextendsPackageDecorator{privatestaticfinaldoubleFRAGILE_FEE8.0;publicFragileDecorator(LogisticsPackagepkg){super(pkg);}OverridepublicStringgetDescription(){returnpkg.getDescription() 【易碎品包装】(气泡膜防震填充);}OverridepublicdoublegetCost(){returnpkg.getCost()FRAGILE_FEE;}}3.7 具体装饰器D货到付款/** * 具体装饰器D货到付款 */publicclassCodDecoratorextendsPackageDecorator{privatedoublecodAmount;privatestaticfinaldoubleCOD_FEE_RATE0.02;privatestaticfinaldoubleMIN_COD_FEE5.0;publicCodDecorator(LogisticsPackagepkg,doublecodAmount){super(pkg);this.codAmountcodAmount;}OverridepublicStringgetDescription(){returnpkg.getDescription() 【货到付款】(代收金额: ¥codAmount);}OverridepublicdoublegetCost(){doublecodFeeMath.max(codAmount*COD_FEE_RATE,MIN_COD_FEE);returnpkg.getCost()codFee;}}3.8 客户端测试publicclassClient{publicstaticvoidmain(String[]args){System.out.println( 智能物流包裹增值服务系统装饰器模式\n);// 场景1普通包裹无增值服务System.out.println(【场景1】普通包裹 - 无增值服务);LogisticsPackagepkg1newBasicPackage(标准包裹北京→上海2kg,12.0);printInfo(pkg1);// 场景2保价 加急System.out.println(\n【场景2】保价 加急);LogisticsPackagepkg2newBasicPackage(标准包裹北京→广州1.5kg,15.0);pkg2newInsuranceDecorator(pkg2,5000.0);pkg2newExpressDecorator(pkg2,ExpressDecorator.ExpressType.NEXT_DAY);printInfo(pkg2);// 场景3易碎品 保价 加急当日达System.out.println(\n【场景3】易碎品 保价 加急当日达);LogisticsPackagepkg3newBasicPackage(标准包裹北京→深圳3kg,18.0);pkg3newFragileDecorator(pkg3);pkg3newInsuranceDecorator(pkg3,10000.0);pkg3newExpressDecorator(pkg3,ExpressDecorator.ExpressType.SAME_DAY);printInfo(pkg3);// 场景4货到付款 保价System.out.println(\n【场景4】货到付款 保价);LogisticsPackagepkg4newBasicPackage(标准包裹北京→杭州1kg,10.0);pkg4newCodDecorator(pkg4,800.0);pkg4newInsuranceDecorator(pkg4,800.0);printInfo(pkg4);// 场景5全都要System.out.println(\n【场景5】全都要易碎品 保价 加急 货到付款);LogisticsPackagepkg5newBasicPackage(标准包裹北京→成都5kg,25.0);pkg5newFragileDecorator(pkg5);pkg5newInsuranceDecorator(pkg5,15000.0);pkg5newExpressDecorator(pkg5,ExpressDecorator.ExpressType.NEXT_DAY);pkg5newCodDecorator(pkg5,2000.0);printInfo(pkg5);}privatestaticvoidprintInfo(LogisticsPackagepkg){System.out.println(-.repeat(70));System.out.println(pkg.getDescription());System.out.printf( 总费用¥%.2f\n,pkg.getCost());System.out.println(-.repeat(70));}}4. 运行结果 智能物流包裹增值服务系统装饰器模式 【场景1】普通包裹 - 无增值服务 ---------------------------------------------------------------------- 标准包裹北京→上海2kg 总费用¥12.00 ---------------------------------------------------------------------- 【场景2】保价 加急 ---------------------------------------------------------------------- 标准包裹北京→广州1.5kg 【保价服务】(声明价值: ¥5000.0) 【加急服务】(NEXT_DAY) 总费用¥177.00 ---------------------------------------------------------------------- 【场景3】易碎品 保价 加急当日达 ---------------------------------------------------------------------- 标准包裹北京→深圳3kg 【易碎品包装】 【保价服务】(声明价值: ¥10000.0) 【加急服务】(SAME_DAY) 总费用¥356.00 ---------------------------------------------------------------------- 【场景4】货到付款 保价 ---------------------------------------------------------------------- 标准包裹北京→杭州1kg 【货到付款】(代收金额: ¥800.0) 【保价服务】(声明价值: ¥800.0) 总费用¥50.00 ---------------------------------------------------------------------- 【场景5】全都要易碎品 保价 加急 货到付款 ---------------------------------------------------------------------- 标准包裹北京→成都5kg 【易碎品包装】 【保价服务】(声明价值: ¥15000.0) 【加急服务】(NEXT_DAY) 【货到付款】(代收金额: ¥2000.0) 总费用¥538.00 ----------------------------------------------------------------------5. 装饰器 vs 继承类的数量对比组合方式需要的子类数量仅 1 种服务4 选 14 个2 种服务组合6 个3 种服务组合4 个4 种服务全组合1 个总计继承方案15 个子类装饰器方案1 基础类 4 装饰器 5 个类 如果增值服务增加到 5 种继承方案需要 31 个子类而装饰器方案只需 6 个类。这就是“组合优于继承”的量化体现。6. 核心角色回顾角色职责对应代码Component定义对象的公共接口LogisticsPackageConcreteComponent被装饰的原始对象BasicPackageDecorator持有 Component 引用默认转发PackageDecoratorConcreteDecorator在转发前后添加自己的行为InsuranceDecorator等7. 装饰器模式 vs 外观模式对比项装饰器模式外观模式意图做加法动态扩展功能做减法简化调用入口接口不改变接口与构件完全一致提供全新的简化接口嵌套可层层嵌套自由组合通常一层不嵌套典型应用Java I/O 流、包裹增值服务SLF4J、JdbcTemplate一句话记忆装饰器是“加料师”——一层层加功能接口不变外观是“一键启动”——把复杂的东西简化成一个入口。BufferedInputStream给FileInputStream加缓冲是装饰器JdbcTemplate封装 JDBC 的复杂调用是外观。8. 装饰器模式的优缺点优点缺点动态扩展功能比继承更灵活产生大量小类增加代码复杂度遵循开闭原则新增装饰器无需修改原有类多层嵌套调试困难调用栈较深装饰器可任意组合实现不同功能组合依赖顺序某些组合对顺序敏感9. 六大设计原则体现原则体现单一职责每种装饰器只负责一种增强核心构件保持纯粹开闭原则新增增值服务只需新增装饰器类里氏替换装饰器与构件实现同一接口可相互替换依赖倒置客户端依赖抽象LogisticsPackage接口接口隔离LogisticsPackage接口精简只有两个方法迪米特法则客户端只知道LogisticsPackage接口不知内部包装层次附 装饰器模式 UML源码物流包裹增值服务场景startuml title Java 23 种设计模式从踩坑到精通 footer 折哥 | 智能物流与Java实战 1. 全局样式配置 skinparam backgroundColor #FEFEFE skinparam shadowing false skinparam classBorderColor #333333 skinparam classFontColor #1A1A1A skinparam classFontSize 14 skinparam noteFontSize 12 skinparam noteFontColor #555555 skinparam arrowColor #555555 skinparam classBackgroundColor #F9F9F9 2. 抽象构件 - 物流包裹 interface LogisticsPackage { getDescription() : String getCost() : double } note right of LogisticsPackage b抽象构件物流包裹/b -- 定义包裹的基本行为描述和费用 被装饰对象和装饰器都实现此接口 end note 3. 具体构件 - 基础包裹 class BasicPackage { - description : String - baseCost : double getDescription() : String getCost() : double } note right of BasicPackage b具体构件基础包裹/b -- 最基础的包裹实现 提供默认的描述和基础运费 end note 4. 抽象装饰器 abstract class PackageDecorator { - pkg : LogisticsPackage getDescription() : String getCost() : double } note right of PackageDecorator b抽象装饰器/b -- 持有 LogisticsPackage 引用 实现与构件相同的接口 默认委托给被装饰对象 end note 5. 具体装饰器 class InsuranceDecorator { getDescription() : String getCost() : double } note right of InsuranceDecorator b具体装饰器A保价服务/b -- 为包裹添加保价功能 费用 原费用 保价费 end note class ExpressDecorator { getDescription() : String getCost() : double } note right of ExpressDecorator b具体装饰器B加急服务/b -- 为包裹添加加急配送 费用 原费用 加急费 end note class FragileDecorator { getDescription() : String getCost() : double } note bottom of FragileDecorator b具体装饰器C易碎品包装/b -- 为包裹添加易碎品特殊包装 费用 原费用 包装费 end note class CodDecorator { getDescription() : String getCost() : double } note bottom of CodDecorator b具体装饰器D货到付款/b -- 为包裹添加代收货款服务 费用 原费用 手续费 end note 6. 关系连线 LogisticsPackage |.. BasicPackage : 实现 LogisticsPackage |.. PackageDecorator : 实现 PackageDecorator o-- LogisticsPackage : 包装/委托 PackageDecorator |-- InsuranceDecorator : 继承 PackageDecorator |-- ExpressDecorator : 继承 PackageDecorator |-- FragileDecorator : 继承 PackageDecorator |-- CodDecorator : 继承 enduml 《Java 23 种设计模式从踩坑到精通》快速导航开篇系列介绍与目录正篇装饰器模式 Decorator —— 比继承更灵活的扩展方式当前番外 · 装饰器模式 × 物流包裹增值服务你在这里创建型模式汇总结构型模式汇总行为型模式汇总 关注《Java 23 种设计模式从踩坑到精通》用 25 篇文章彻底吃透设计模式。福利预告全系列代码及 UML 源码将在完结时统一打包开放点击「关注」「收藏」第一时间获取。 除了设计模式我也在深挖智能物流实战WMS、托盘调度、机器学习落地。欢迎点击头像看看专栏 《出版社物流WMS智能调度实战》、《电商多平台电子面单对接实战》。技术相通思路可鉴。

相关新闻

bootstrap + requireJS+ director+ knockout + web API = 一个时髦的单页程序

bootstrap + requireJS+ director+ knockout + web API = 一个时髦的单页程序

Bootstrap RequireJS Director Knockout Web API 一个时髦的单页程序 在现代前端开发中,单页应用(SPA)已经成为主流。本文将实战演示如何利用 Bootstrap、RequireJS、Director、Knockout 和 Web API 构建一个时髦的单页程序。这些技术各…

2026/10/19 15:26:58 阅读更多 →
Desktop-Cube多显示器设置指南:解决X11与Wayland下的视角问题

Desktop-Cube多显示器设置指南:解决X11与Wayland下的视角问题

Desktop-Cube多显示器设置指南:解决X11与Wayland下的视角问题 【免费下载链接】Desktop-Cube 🧊 Indulge in nostalgia with useless 3D effects. 项目地址: https://gitcode.com/gh_mirrors/de/Desktop-Cube Desktop-Cube是一款能为Linux桌面环境…

2026/10/20 11:14:45 阅读更多 →
序列化陷阱:Protobuf与gRPC版本兼容性问题调试指南

序列化陷阱:Protobuf与gRPC版本兼容性问题调试指南

序列化问题的隐蔽性:一个Kafka反序列化失败案例 在微服务架构中,服务之间通过序列化协议交换数据。看似无关紧要的字段添加、枚举值变更,可能在生产环境中引发静默故障。考虑这样一个场景:订单服务向Kafka的orders.created主题发布事件,而履约服务消费该事件并将订单存入…

2026/10/20 7:36:46 阅读更多 →

最新新闻

Vidu Q4 Preview深度评测:AI视频生成迈向生产力工具的关键一步

Vidu Q4 Preview深度评测:AI视频生成迈向生产力工具的关键一步

最近几个创作群里讨论最多的,绕不开Vidu Q4 Preview。做AI视频的同仁应该都有感觉,这个赛道现在卷到什么程度:几乎每隔几天就有新版本、新模型冒出来,但大多是“看起来厉害、一上手就露馅”。Vidu Q4 Preview这次放出的内容&#…

2026/10/21 0:33:56 阅读更多 →
AI在芯片设计中的黑盒困局:一次IR压降事故带来的反思

AI在芯片设计中的黑盒困局:一次IR压降事故带来的反思

芯片世界里,有一句话最近被工程师们重复得越来越多:AI赢了棋局,却讲不出思路。就在上个月,我拿着一个AI辅助完成的block级floorplan给物理设计团队,线长比上一版少了12%,拥塞图也干净得不像话。结果等IR压降…

2026/10/21 0:33:56 阅读更多 →
计算智能体入门:从状态机到Q-learning的决策实现

计算智能体入门:从状态机到Q-learning的决策实现

1. 从零理解计算智能体的核心骨架计算智能体这个概念,听起来像是学术论文里的术语,但把它拆开来看,其实就是一个能感知环境、做出决策、执行动作的软件实体。你可以把它想象成一个在迷宫里找路的小机器人,它能看到周围哪里有墙、哪…

2026/10/21 0:33:55 阅读更多 →
MoE混合专家模型:稀疏激活如何破解大模型容量与成本困局

MoE混合专家模型:稀疏激活如何破解大模型容量与成本困局

从Transformer诞生起,前馈网络(FFN)就是"又笨重又绕不开"的角色:它占了模型近三分之二的参数量,但每个token都得老老实实把所有参数过一遍。你扩大模型容量,推理成本就跟着线性往上涨&#xff0c…

2026/10/21 0:33:55 阅读更多 →
开源30B模型硬刚闭源旗舰:科研场景实测与本地部署指南

开源30B模型硬刚闭源旗舰:科研场景实测与本地部署指南

最近开源圈又热闹起来了,一个主打科研场景的30B级别开源模型,直接放话要跟两家头部闭源大模型掰手腕。我当时第一反应是“又来一个刷榜的”,但仔细看完技术报告和社区实测数据之后,觉得这波还真不是营销话术,确实有货。…

2026/10/21 0:33:55 阅读更多 →
LLM强化学习全解析:PPO、GRPO、DPO等五类方法选型与实践

LLM强化学习全解析:PPO、GRPO、DPO等五类方法选型与实践

做LLM训练绕不开强化学习这块,标题里这五个字看着简单,真要分类起来其实挺讲究的。我前几年刚开始接触大模型对齐时,也把PPO、DPO、GRPO这些名词搞得晕头转向,后来用了一段时间才理清楚它们之间的血缘关系和适用场景。这篇博文就把…

2026/10/21 0:32:54 阅读更多 →

日新闻

统一内存+量化:锐龙AI Max PRO 400如何本地跑320B大模型

统一内存+量化:锐龙AI Max PRO 400如何本地跑320B大模型

这段时间硬件圈和AI圈都在传一个事:AMD锐龙AI Max PRO 400系列把最高192GB统一内存搬进了PC形态的产品里,按宣传口径,本地跑320B参数的大家伙不再是纸面概念。我第一反应倒不是“内存真大”,而是这话背后的含金量——它把过去只能…

2026/10/21 0:00:11 阅读更多 →
AI助手深度使用体验:对话质量、长文本处理与指令遵循如何留住用户

AI助手深度使用体验:对话质量、长文本处理与指令遵循如何留住用户

1. 从“封号重开”这个行为说起:用户到底在追求什么“封号重开”这四个字,放在任何产品语境里都带着一股决绝的味道。一个账号从零开始积累使用记录、调教偏好、沉淀工作流,背后是大量的时间和精力成本。正常情况下,没有人愿意主动…

2026/10/21 0:00:11 阅读更多 →
ROS中YOLOv3工程落地:通信同步、坐标变换与性能调优

ROS中YOLOv3工程落地:通信同步、坐标变换与性能调优

1. 为什么在ROS里硬刚YOLOv3不是“炫技”,而是工程落地的必经之路我第一次在某高校机器人实验室看到那台搭载Jetson TX2的移动底盘原地打转、反复识别错垃圾桶时,就意识到:单纯跑通darknet_ros demo和真正让YOLOv3在ROS里“干活”&#xff0c…

2026/10/21 0:00:11 阅读更多 →

周新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型(GMM)的语音识别系统,很多人第一反应就是一堆数学公式,觉得门槛很高。但如果你真的在MATLAB里把它落地跑通,会发现核心其实就是三件事:把声音变成特征向量,用GMM给每个类别…

2026/10/20 10:02:13 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人,但落到具体操作上,无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程:对图像做反白、改调色板、彩色转灰度,最后把像素数据导出成txt文件。单看每一…

2026/10/20 20:43:59 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词,你会发现一个很有意思的现象:每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型,有人指的是运动控制系统,还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/20 2:50:50 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 10:02:08 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 9:50:17 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 9:50:45 阅读更多 →