蓝凌EKP18产品:设计模式如何撑起一个流程引擎
前面 我们讲了相关的流程引擎概念,现在我们讲投我们在设计流程引擎的时候,我们使用到的哪些设计模式。我们从设计模式的使用上来真正架构一个产品。我们把流程引擎从概念讲到实现。细心的读者可能发现了:每一课里都在出现"模式"——策略模式、模板方法、观察者、门面、适配器…
但我们一直没有系统地讲它们。为什么?
因为设计模式是横向知识。它不是"某一课的内容",而是"贯穿所有课的那条暗线"。你单独看某一课,看到的只是"这里用了策略模式";只有把整个引擎的模式拉出来并列,才能看清:
设计模式不是点缀,而是流程引擎之所以"能扩展、能解耦、能并发"的根本原因。
这个系列,就是把这根暗线抽出来讲透。
一、先看问题:流程引擎难在哪
要理解模式的价值,先理解流程引擎面临的五个核心难题:
| 难题 | 具体表现 |
|---|---|
| 变化点太多 | 审批规则、节点类型、聚合策略、操作动作,每个客户都不一样 |
| 状态复杂 | 一条流程有六种状态、多层执行路径、并发分支 |
| 流程长 | 一次审批可能跨天跨人,不能用一个长事务包住 |
| 要可扩展 | 客户定制不能改核心代码 |
| 要可观测 | 每一步都要留痕、可追溯 |
这五个难题,恰好对应了四个经典的设计模式族。 这不是巧合——流程引擎这类"高度可配置的业务系统",天然就是设计模式的练兵场。
二、模式地图:一张图看懂全局
┌─────────────────────────────────────────────────────────────┐ │ 流程引擎设计模式地图 │ │ │ │ 【难题一】变化点太多 → 扩展解耦组 │ │ ├─ 策略模式 ── 换算法不改调用方(聚合策略) │ │ ├─ 工厂+注册表 ── 按名字取实现(原子操作、类型管理) │ │ ├─ 适配器模式 ── 隔离变化(EngineWire 契约) │ │ └─ 门面模式 ── 简化复杂度(服务管理器) │ │ │ │ 【难题二】状态复杂 → 流程流转组 │ │ ├─ 命令模式 ── 把操作对象化(原子操作) │ │ ├─ 状态模式 ── 用状态驱动行为(六状态机) │ │ ├─ 组合模式 ── 树形结构统一处理(执行路径树) │ │ └─ 备忘录模式 ── 保存/恢复现场(同步中断) │ │ │ │ 【难题三/五】要可观测 → 行为扩展组 │ │ ├─ 观察者模式 ── 事件发布订阅(关键节点广播) │ │ ├─ 责任链模式 ── 链式处理(监听器链) │ │ └─ 模板方法模式 ── 固定骨架、填充细节(操作行为) │ │ │ │ 【难题四】要可扩展 → 结构包装组 │ │ ├─ 桥接模式 ── 抽象与实现分离(执行路径包裹) │ │ ├─ 代理模式 ── 中间层增强(节点代理) │ │ └─ 访问者模式 ── 操作与结构分离(遍历器) │ └─────────────────────────────────────────────────────────────┘
读图要点:每个分组对应一个难题,每个模式解决难题的一个侧面。它们不是孤立的——同一个功能往往需要几个模式配合(比如聚合策略 = 策略模式 + 观察者模式的两层决策)。
三、四条主线:模式如何协同
3.1 主线一:可扩展性从哪来
客户要定制 ↓ 必须有不改核心就能扩展的地方 ↓ ├─ 定义接口(适配器模式)── EngineWire、ActivityBehaviour ├─ 按名取实现(工厂+注册表)── Operation、TypeManager ├─ 运行时选算法(策略模式)── JoinStrategy └─ 对外给单一入口(门面模式)── ProcessServiceManager
核心思想:接口定义契约,实现承载变化,注册表负责查找,门面负责收口。
3.2 主线二:状态机怎么驱动
流程要流转 ↓ 必须有清晰的"当前位置"和"下一步" ↓ ├─ 把流转动作封装成对象(命令模式)── AtomicOperation ├─ 用状态描述位置(状态模式)── ACTIVE/WAITING/ENDING/ENDED ├─ 用树表示并发(组合模式)── 执行路径树 └─ 支持中断恢复(备忘录模式)── 同步事件保存现场
核心思想:状态显式化 + 动作对象化 + 结构树形化 + 现场可恢复。
3.3 主线三:可观测怎么实现
流程要留痕、要能插业务逻辑 ↓ 必须在关键节点"广播"和"拦截" ↓ ├─ 关键节点发事件(观察者模式)── EngineEvent ├─ 多个监听器依次处理(责任链模式)── IEventListener 链 └─ 统一流程骨架(模板方法)── prepare → execute
核心思想:主线只做核心逻辑,横切逻辑挂在事件上。
3.4 主线四:解耦怎么做
PVM 层要独立于具体引擎实现 ↓ 必须有一层"包裹"和"中介" ↓ ├─ 抽象与实现分离(桥接模式)── ExecutionWraper ├─ 中间层增强(代理模式)── ProxyNodeDefinition └─ 遍历与结构分离(访问者模式)── ExecutionVisitor
核心思想:加一层间接,换一份自由。
四、按重要性排序:先学哪些
14 个模式不是同等重要。如果时间有限,按这个顺序学:
| 优先级 | 模式 | 理由 |
|---|---|---|
| ★★★ 必学 | 策略模式 | 扩展的典型范式,最直观 |
| ★★★ 必学 | 命令模式 | 理解"原子操作"的钥匙 |
| ★★★ 必学 | 状态模式 | 理解流程流转的钥匙 |
| ★★★ 必学 | 观察者模式 | 理解事件驱动的钥匙 |
| ★★☆ 重要 | 模板方法 | 理解行为骨架 |
| ★★☆ 重要 | 组合模式 | 理解并发分支 |
| ★★☆ 重要 | 适配器 | 理解分层解耦 |
| ★★☆ 重要 | 工厂+注册表 | 理解扩展注册 |
| ★☆☆ 补充 | 门面、责任链、桥接、备忘录、代理、访问者 | 理解细节 |
前三篇(策略、命令、状态)+ 观察者,是最核心的四个。 掌握了这四个,流程引擎的设计思想就抓住了大半。
五、每篇怎么讲
为了保证系列的一致性,每篇统一用这个结构:
1. 业务问题 这个模式要解决什么真实问题?(用流程场景讲) 2. 模式解法 模式的核心思想和标准结构是什么? 3. 源码证据 EKP 里怎么用的?贴关键代码,画结构图 4. 设计启示 为什么这么设计?不用模式会怎样? 5. 业务价值 对业务、对交付、对维护意味着什么?
读法建议:先读"业务问题"和"业务价值"两节——它们回答"为什么需要"。技术细节可以快速扫过,需要时再回头细看源码部分。
六、一个重要的提醒
不要为了用模式而用模式。
这 14 个模式之所以出现在流程引擎里,是因为流程引擎真的面临那些难题。如果换一个简单的 CRUD 系统,硬套这些模式,只会让代码更复杂。
判断标准很简单:
当"变化点"反复出现、且每次变化都要改同一处代码时,才需要考虑用模式把那处变化隔离出去。
模式是"应对变化的经验总结",不是"炫技的工具箱"。
七、系列导航
| # | 篇名 | 核心问题 |
|---|---|---|
| 00 | 开篇:模式地图(本篇) | 全局认识 |
| 01 | 策略模式 | 聚合策略如何做到可配可换 |
| 02 | 工厂 + 注册表模式 | 引擎如何按名字找到实现 |
| 03 | 适配器模式 | PVM 如何独立于具体引擎 |
| 04 | 门面模式 | 复杂引擎如何对外给简单入口 |
| 05 | 命令模式 | 原子操作为什么是对象 |
| 06 | 状态模式 | 执行路径的六种状态如何驱动行为 |
| 07 | 组合模式 | 并行分支为什么用树表示 |
| 08 | 备忘录模式 | 同步事件如何保存和恢复现场 |
| 09 | 观察者模式 | 事件驱动如何串联整个流程 |
| 10 | 责任链模式 | 多个监听器如何依次生效 |
| 11 | 模板方法模式 | 操作行为的统一骨架 |
| 12 | 桥接模式 | 执行路径为什么要"包裹" |
| 13 | 代理 + 访问者模式 | 中间层与遍历分离 |
八、业务价值
8.1、看懂"改一处不动全身"的能力从哪来
业务方最常问:"为什么改个审批规则要等两周?"
答案往往不是开发慢,而是代码没有为"变化"设计。设计模式让变化点被隔离在独立的位置——改聚合规则不动流程驱动,加操作按钮不动节点逻辑。
业务价值:需求响应速度。 有模式 = 改一个类;没模式 = 改核心 + 全量回归 + 高风险发版。
8.2、让"客户定制"不变成"产品失控"
一套标准产品要卖 100 个客户,每个客户都想改点东西。如果每个改动都往核心代码里塞,产品一年后就没法维护了。
设计模式提供的是**"扩展位"**——接口、策略、注册表、事件。客户在扩展位上加东西,核心保持干净。
业务价值:产品可规模化交付。 这是 SaaS/企业软件能否做大的分水岭。
8.3、降低"人走代码烂"的风险
设计模式是行业通用语言。"这里用了策略模式",新人三天就能看懂。反面是一个五千行的 if-else,只有原作者能改,他一走代码就烂。
业务价值:团队可替换性。 人员流动是必然的,代码的"可理解性"决定团队能不能接力。
8.4、故障定位快,减少业务中断
模式把职责分清楚了:策略只管算合不合流,Finder 只管找数据,监听器只管发通知。出了问题,日志一看就知道在哪个环节。
业务价值:故障恢复时间(MTTR)。 定位快一小时,业务就少中断一小时。
8.5、给技术人一条可复制的成长路径
对读者(也就是课程受众)来说:
- 光看概念,模式是空洞的
- 光看代码,不知道好在哪
- "问题 → 模式 → 源码 → 启示" 这个结构,给了一条可复制的学习路径
更多推荐



所有评论(0)