Java 23 种设计模式:从踩坑到精通 | 番外:编排器+策略模式在多平台电子面单中的实战(含性能压测)
Java 23 种设计模式:从踩坑到精通 | 番外:编排器+策略模式在多平台电子面单中的实战(含性能压测)
一、缘起:当设计模式缺失,代码如何走向“屎山”
策略模式是Java设计模式中常用的行为模式,但很多开发者只知道用if-else判断平台类型,却不知道如何优雅地解耦流程与算法。本文将通过一个真实的多平台电子面单对接案例,展示如何将策略模式与编排器结合,实现“流程与策略完全解耦”,并落实开闭原则、单一职责原则。
在电商WMS系统中,电子面单获取是发货环节的核心。随着业务扩展(支持顺丰多产品、抖音、京东等),原始的对接代码逐渐腐化:一个方法膨胀到250+行,内部变量名从obj1排到obj16,大量if-else判断平台类型,任何修改都如履薄冰。
// 伪代码示例(原始“上帝方法”)privatevoidgetWaybill(Stringplatform,...){if("TM".equals(platform)){// 构建奇门请求}elseif("DY".equals(platform)){// 构建抖音请求}// ... 后续流程混杂在一起}设计模式缺失的痛点:
- 流程与算法耦合:取号流程(构建请求 → 调用API → 解析响应 → 保存)与平台特定实现混在一起,无法复用。
- 扩展困难:新增平台需要复制整个方法,修改多处。
- 违反开闭原则:每新增一个平台,都要修改核心方法。
- 测试困难:无法对独立算法进行单元测试。
我们决定彻底重构,采用编排器 + 策略模式。
二、设计模式选型:为什么是“编排器+策略”?
2.1 策略模式回顾
策略模式定义一系列算法,将每个算法封装起来,并使它们可以相互替换。它让算法的变化独立于使用算法的客户。
2.2 为什么不用单纯的策略模式?
单纯的策略模式只封装算法,但流程控制(日志、异常处理、事务)仍会重复出现。我们需要一个编排器来固定流程骨架。
2.3 为什么不用模板方法模式?
模板方法模式通过继承复用流程,但子类必须继承基类,强耦合。且一旦需要修改流程,所有子类受影响。
2.4 最终方案:编排器 + 策略模式
- 编排器:作为“指挥官”,固定取号流程(构建请求 → 调用API → 判断成功 → 解析响应 → 保存绑定),不关心具体平台。
- 策略接口:
RequestStrategy(请求构建)、ParseStrategy(响应解析)、ExceptionStrategy(业务异常判断)。 - 上下文对象:封装订单、已有件数、产品代码等参数,避免策略方法参数膨胀。
这样,流程与算法彻底分离,新增平台只需实现三个策略类,无需修改编排器,完美符合开闭原则。
三、架构设计:类图与时序图
3.1 多平台电子面单中的实战核心类图
角色说明:
- Context(上下文):
WaybillContext充当策略运行时的环境。 - Strategy(策略接口):三个接口分别定义不同职责。
- ConcreteStrategy(具体策略):如
QiMenRequestStrategy,实现平台特定算法。 - Orchestrator(编排器):
WaybillFetchTemplate固定流程骨架。 - Factory(工厂):
StrategyFactory根据平台编码返回策略实例。
3.2 多平台电子面单中的实战时序图
关键点:编排器作为纯流程控制,不依赖任何平台具体实现;策略完全由工厂注入。
四、代码实现(JDK 1.6 兼容)
4.1 编排器
publicclassWaybillFetchTemplate{privatefinalRequestStrategyrequestStrategy;privatefinalParseStrategyparseStrategy;privatefinalExceptionStrategyexceptionStrategy;privatefinalApiInvokerapiInvoker;privatefinalWaybillPersistencepersistence;publicWaybillFetchTemplate(RequestStrategyreq,ParseStrategyparse,ExceptionStrategyex,ApiInvokerinvoker,WaybillPersistencepersist){this.requestStrategy=req;this.parseStrategy=parse;this.exceptionStrategy=ex;this.apiInvoker=invoker;this.persistence=persist;}publicbooleanexecute(WaybillContextctx){try{// 1. 构建请求(策略)Objectrequest=requestStrategy.buildRequest(ctx);// 2. 调用APIStringresponse=apiInvoker.invoke(request,ctx);// 3. 业务异常判断(策略)if(!exceptionStrategy.isBusinessSuccess(response)){StringerrMsg=exceptionStrategy.extractErrorMsg(response);markException(ctx.getTicket(),errMsg);returnfalse;}// 4. 解析响应(策略)List<WaybillDetail>details=parseStrategy.parseResponse(response,ctx);if(details==null||details.isEmpty()){markException(ctx.getTicket(),"未获取到运单号");returnfalse;}// 5. 保存绑定persistence.saveAndBind(ctx.getTicket(),details,ctx.getExsitJianNum()==0);returntrue;}catch(Exceptione){handleException(ctx.getTicket(),e);returnfalse;}}}4.2 策略实现示例(奇门)
publicclassQiMenRequestStrategyimplementsRequestStrategy{@OverridepublicObjectbuildRequest(WaybillContextctx){// 构建奇门特有的 WaybillCloudPrintApplyNewRequest// 复用原有业务逻辑,但只关注参数组装returnbuildQiMenRequest(ctx.getTicket(),ctx.getExsitJianNum(),ctx.getProductCode());}}publicclassQiMenParseStrategyimplementsParseStrategy{@OverridepublicList<WaybillDetail>parseResponse(Stringresponse,WaybillContextctx){// 解析奇门响应中的 waybill_cloud_print_responsereturnQiMenResponseParser.parse(response);}}publicclassQiMenExceptionStrategyimplementsExceptionStrategy{@OverridepublicbooleanisBusinessSuccess(Stringresponse){return!response.contains("\"error\"")&&response.contains("\"waybill_cloud_print_response\"");}@OverridepublicStringextractErrorMsg(Stringresponse){// 提取错误信息}}4.3 策略工厂
publicclassStrategyFactory{privateMap<String,RequestStrategy>requestMap=newHashMap<String,RequestStrategy>();privateMap<String,ParseStrategy>parseMap=newHashMap<String,ParseStrategy>();privateMap<String,ExceptionStrategy>exceptionMap=newHashMap<String,ExceptionStrategy>();publicStrategyFactory(){// 注册奇门平台requestMap.put("TM",newQiMenRequestStrategy());parseMap.put("TM",newQiMenParseStrategy());exceptionMap.put("TM",newQiMenExceptionStrategy());// 注册抖音平台...}publicRequestStrategygetRequestStrategy(StringplatformCode){returnrequestMap.get(platformCode);}// 类似获取其他策略}五、设计模式收益分析
5.1 开闭原则(对扩展开放,对修改封闭)
新增一个平台(如京东)时,只需创建JingdongRequestStrategy、JingdongParseStrategy、JingdongExceptionStrategy三个类,并在工厂中注册,编排器WaybillFetchTemplate一行代码都不用改。
5.2 单一职责原则
每个策略类只负责一项任务:构建请求、解析响应或异常判断。编排器只负责流程控制。
5.3 依赖倒置
上层模块(编排器)依赖抽象(策略接口),不依赖具体实现。
5.4 可测试性
每个策略类可以独立测试,编排器可以通过注入Mock策略进行测试。
六、性能与质量数据
| 指标 | 重构前 | 重构后 |
|---|---|---|
| 核心流程代码行数 | 250+ 行 | 编排器80行 + 策略类60行 |
| 重复代码(跨平台) | 60% | 0% |
| 单元测试覆盖 | <5% | 80%+ |
| 新增平台接入时间 | 2-3天 | 0.5天 |
| 10包裹平均响应时间 | 850 ms | 60 ms |
| 吞吐量(TPS) | 22 | 350 |
压测环境:4核CPU/8GB内存/Oracle 11g/JDK 1.6,并发20线程。
七、踩坑与避坑指南(设计模式落地常见问题)
| 现象 | 原因 | 解决方案 |
|---|---|---|
| 策略工厂返回null | 平台编码未注册 | 使用防御性设计,返回默认策略或抛明确异常 |
| 策略类中重复代码 | 不同平台有相似构建逻辑 | 提取公共工具类,避免复制粘贴 |
| 编排器过于臃肿 | 把通用逻辑(如重试)放进了编排器 | 抽离到单独的ApiInvoker或RetryTemplate |
| 上下文对象膨胀 | 传递了过多参数 | 使用Map<String,Object>扩展属性,或拆分为多个上下文对象 |
八、总结
通过“编排器 + 策略模式”重构多平台电子面单对接,我们实现了:
- ✅流程与算法彻底解耦:编排器固定骨架,策略独立变化。
- ✅完美遵循开闭原则:新增平台零修改核心流程。
- ✅高性能:消除N+1查询,缓存配置,响应时间降低90%。
- ✅高可测试性:每个策略类可独立单元测试。
设计模式不是银弹,但用对地方能让代码从“能跑就行”进化为“可维护的资产”。希望本文的实战经验能为你在实际项目中应用设计模式提供借鉴。
🧭 《Java 23 种设计模式:从踩坑到精通》快速导航
开篇:系列介绍与目录
创建型模式汇总:单例、工厂、建造者、原型
结构型模式汇总:适配器、装饰器、代理……
行为型模式汇总:观察者、策略、模板方法……
🔔 关注《Java 23 种设计模式:从踩坑到精通》,用 25 篇文章彻底吃透设计模式。
📦福利预告:全系列代码及 UML 源码将在完结时统一打包开放,点击「关注」「收藏」第一时间获取。
🚀下一篇:装饰器模式 —— 比继承更灵活的扩展方式,你用过吗?🚧 即将发布,敬请关注!
📌 除了设计模式,我也在深挖智能物流实战(WMS、托盘调度、机器学习落地)。欢迎点击头像,看看专栏 《出版社物流WMS智能调度实战》、《电商多平台电子面单对接实战》。技术相通,思路可鉴。
