抽象基类封装节点id、状态等通用字段及execute()流程,强制子类实现canapprove()、onapproved()、onrejected();具体节点如departmentmanagernode、financefinalnode仅重写差异逻辑;通过工厂或spring按类型创建实例。
用最常用的继承写法实现工作流引擎中不同审批节点类,核心是定义一个抽象的基类封装通用逻辑(如节点id、审批动作、状态流转),再让具体审批节点(如“部门经理审批”“hr复核”“财务终审”)继承它并重写关键行为。
定义抽象审批节点基类
基类不直接实例化,只规定子类必须实现的方法和共享的状态字段。例如:
-
字段统一管理:如
nodeId、status(待审批/已通过/已驳回)、approver、createTime -
强制子类实现:用
abstract声明canApprove()(权限校验)、onApproved()(通过后操作)、onRejected()(驳回后操作) -
提供默认能力:比如
execute()方法可封装“先校验→再执行→更新状态→触发下一流程”的标准流程,避免重复代码
声明具体审批节点子类
每个业务节点继承基类,只专注自身差异逻辑。例如:
-
DepartmentManagerNode:重写
canApprove()判断当前用户是否为该部门经理;onApproved()中自动将流程推送给 HR 节点 -
FinanceFinalNode:在
onApproved()中调用账务系统接口锁定预算;canApprove()额外检查申请金额是否超阈值 - 所有子类无需重复处理日志记录、状态持久化、异常包装——这些由基类统一完成
配合工厂或配置驱动节点创建
运行时根据流程定义动态生成对应节点对象,常见做法:
- 用 简单工厂:传入节点类型字符串(如
"finance_final"),返回对应子类实例 - 结合 Spring @Component + @Qualifier:每个子类加注解,用
ApplicationContext.getBean()按类型或名称获取 - 从数据库读取节点配置(type 字段),映射到具体类名,再通过反射或 BeanFactory 实例化
注意设计边界与扩展性
继承适合“本质相同、行为略有差异”的场景。要避免以下问题:
- 不把审批策略(如会签/或签/加签)塞进继承体系——更适合用策略模式组合进来
- 节点间跳转规则(如“驳回后回到申请人”还是“回到上一节点”)不应由节点类自己决定,而由流程引擎控制
- 若某类节点需要完全不同的生命周期(如“自动审批节点”不需人工介入),可考虑接口隔离,而非强行继承同一基类










