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智能调度实战》、《电商多平台电子面单对接实战》。技术相通思路可鉴。