Java 23 种设计模式:从踩坑到精通 | 番外:编排器+策略模式在多平台电子面单中的实战(含性能压测)
一、缘起:当设计模式缺失,代码如何走向"屎山"
策略模式是Java设计模式中常用的行为模式,但很多开发者只知道用 if-else 判断平台类型,却不知道如何优雅地解耦流程与算法。本文将通过一个真实的多平台电子面单对接案例,展示如何将策略模式与编排器结合,实现"流程与策略完全解耦",并落实开闭原则、单一职责原则。
在电商WMS系统中,电子面单获取是发货环节的核心。随着业务扩展(支持顺丰多产品、抖音、京东等),原始的对接代码逐渐腐化:一个方法膨胀到250+行 ,内部变量名从 obj1 排到 obj16,大量 if-else 判断平台类型,任何修改都如履薄冰。
java
// 伪代码示例(原始"上帝方法")
private void getWaybill(String platform, ...) {
if ("TM".equals(platform)) {
// 构建奇门请求
} else if ("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 编排器
java
public class WaybillFetchTemplate {
private final RequestStrategy requestStrategy;
private final ParseStrategy parseStrategy;
private final ExceptionStrategy exceptionStrategy;
private final ApiInvoker apiInvoker;
private final WaybillPersistence persistence;
public WaybillFetchTemplate(RequestStrategy req, ParseStrategy parse,
ExceptionStrategy ex, ApiInvoker invoker,
WaybillPersistence persist) {
this.requestStrategy = req;
this.parseStrategy = parse;
this.exceptionStrategy = ex;
this.apiInvoker = invoker;
this.persistence = persist;
}
public boolean execute(WaybillContext ctx) {
try {
// 1. 构建请求(策略)
Object request = requestStrategy.buildRequest(ctx);
// 2. 调用API
String response = apiInvoker.invoke(request, ctx);
// 3. 业务异常判断(策略)
if (!exceptionStrategy.isBusinessSuccess(response)) {
String errMsg = exceptionStrategy.extractErrorMsg(response);
markException(ctx.getTicket(), errMsg);
return false;
}
// 4. 解析响应(策略)
List<WaybillDetail> details = parseStrategy.parseResponse(response, ctx);
if (details == null || details.isEmpty()) {
markException(ctx.getTicket(), "未获取到运单号");
return false;
}
// 5. 保存绑定
persistence.saveAndBind(ctx.getTicket(), details, ctx.getExsitJianNum() == 0);
return true;
} catch (Exception e) {
handleException(ctx.getTicket(), e);
return false;
}
}
}
4.2 策略实现示例(奇门)
java
public class QiMenRequestStrategy implements RequestStrategy {
@Override
public Object buildRequest(WaybillContext ctx) {
// 构建奇门特有的 WaybillCloudPrintApplyNewRequest
// 复用原有业务逻辑,但只关注参数组装
return buildQiMenRequest(ctx.getTicket(), ctx.getExsitJianNum(), ctx.getProductCode());
}
}
public class QiMenParseStrategy implements ParseStrategy {
@Override
public List<WaybillDetail> parseResponse(String response, WaybillContext ctx) {
// 解析奇门响应中的 waybill_cloud_print_response
return QiMenResponseParser.parse(response);
}
}
public class QiMenExceptionStrategy implements ExceptionStrategy {
@Override
public boolean isBusinessSuccess(String response) {
return !response.contains("\"error\"") && response.contains("\"waybill_cloud_print_response\"");
}
@Override
public String extractErrorMsg(String response) {
// 提取错误信息
}
}
4.3 策略工厂
java
public class StrategyFactory {
private Map<String, RequestStrategy> requestMap = new HashMap<String, RequestStrategy>();
private Map<String, ParseStrategy> parseMap = new HashMap<String, ParseStrategy>();
private Map<String, ExceptionStrategy> exceptionMap = new HashMap<String, ExceptionStrategy>();
public StrategyFactory() {
// 注册奇门平台
requestMap.put("TM", new QiMenRequestStrategy());
parseMap.put("TM", new QiMenParseStrategy());
exceptionMap.put("TM", new QiMenExceptionStrategy());
// 注册抖音平台...
}
public RequestStrategy getRequestStrategy(String platformCode) {
return requestMap.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智能调度实战》、《电商多平台电子面单对接实战》。技术相通,思路可鉴。