如何正确实现表单状态的状态机模式?现有实现存疑求指导
表单状态机模式的改进方案
你的现有实现没感受到状态机的优势,核心问题是职责边界模糊、状态跳转逻辑耦合严重、动作触发不清晰,以下是针对性的改进思路和代码示例:
现有实现的核心问题
- 状态类既判断业务规则又处理跳转,还重复设置枚举值与状态对象,职责混乱
- 状态之间互相依赖(比如一个状态类直接实例化另一个状态类),新增/修改状态时要改多个类,违反开闭原则
- 初始状态硬编码为Draft,没考虑表单从数据库加载时已有状态的场景
- 用一个笼统的
HandleStateTransition处理所有跳转,无法区分用户的具体动作(提交、审批、拒绝)
改进思路
- 明确动作与状态的对应关系:状态机的触发应该是用户的具体动作(提交表单、经理审批、技术审批等),而非状态自己主动跳转
- 状态类只负责自身允许的动作:每个状态类只实现当前状态下能响应的动作,以及对应的状态转换逻辑
- 分离状态枚举与状态类的映射:用字典维护枚举到状态类的映射,避免硬编码
- 业务规则集中管理:把金额、表单类型这类判断逻辑集中在对应动作的处理中,避免在多个状态类中重复编写
改进后的代码实现
1. 修正状态机服务类(FormStateService)
public class FormStateService { private readonly Dictionary<State, FormState> _stateMap; private FormState _currentState; private readonly Form _form; public FormStateService(Form form) { _form = form; // 初始化状态映射,枚举与状态类一一对应 _stateMap = new Dictionary<State, FormState> { { State.Draft, new DraftState() }, { State.WaitForApprovalManager, new WaitForApprovalManagerState() }, { State.WaitForApprovalTech, new WaitForApprovalTechState() }, { State.WaitForApprovalFinance, new WaitForApprovalFinanceState() }, { State.Afgekeurd, new DeniedState() }, { State.Afgerond, new ApprovedState() } }; // 根据表单当前状态初始化状态对象,支持从数据库加载的已有表单 SetCurrentState((State)_form.State); } private void SetCurrentState(State state) { _currentState = _stateMap[state]; _currentState.SetContext(_form); } // 暴露具体的动作方法,对应用户实际操作 public void SubmitForm() { var newState = _currentState.OnSubmit(); UpdateFormState(newState); } public void ManagerApprove() { var newState = _currentState.OnManagerApproval(); UpdateFormState(newState); } public void TechApprove() { var newState = _currentState.OnTechApproval(); UpdateFormState(newState); } public void FinanceApprove() { var newState = _currentState.OnFinanceApproval(); UpdateFormState(newState); } public void DenyForm() { var newState = _currentState.OnDeny(); UpdateFormState(newState); } private void UpdateFormState(State? newState) { if (newState.HasValue) { _form.State = (int)newState.Value; SetCurrentState(newState.Value); } } public State GetCurrentState() { return (State)_form.State; } }
2. 重新定义抽象状态类(FormState)
public abstract class FormState { protected Form _form; public void SetContext(Form form) { _form = form; } // 定义各个动作的抽象方法,状态类按需实现(不需要的动作可抛出异常或返回null) public abstract State? OnSubmit(); public abstract State? OnManagerApproval(); public abstract State? OnTechApproval(); public abstract State? OnFinanceApproval(); public abstract State? OnDeny(); }
3. 具体状态类示例(DraftState)
public class DraftState : FormState { public override State? OnSubmit() { // 集中处理提交时的业务规则 if (_form.TotalPay < 500) { return State.WaitForApprovalFinance; } else if (_form.TotalPay >= 500 && _form.IsTechForm) { // 技术类且金额>=500,先进入经理审批 return State.WaitForApprovalManager; } else if (_form.TotalPay >= 500) { return State.WaitForApprovalManager; } return null; } // 草稿状态下不允许审批操作,抛出异常明确禁止 public override State? OnManagerApproval() => throw new InvalidOperationException("草稿表单无法进行经理审批"); public override State? OnTechApproval() => throw new InvalidOperationException("草稿表单无法进行技术审批"); public override State? OnFinanceApproval() => throw new InvalidOperationException("草稿表单无法进行财务审批"); public override State? OnDeny() => throw new InvalidOperationException("草稿表单无法拒绝"); }
4. 具体状态类示例(WaitForApprovalManagerState)
public class WaitForApprovalManagerState : FormState { public override State? OnSubmit() => throw new InvalidOperationException("已提交的表单无法重复提交"); public override State? OnManagerApproval() { // 经理审批通过后,根据规则跳转 if (_form.IsTechForm) { // 技术类表单转到技术审批 return State.WaitForApprovalTech; } else { // 非技术类转到财务审批 return State.WaitForApprovalFinance; } } public override State? OnTechApproval() => throw new InvalidOperationException("未完成经理审批,无法进行技术审批"); public override State? OnFinanceApproval() => throw new InvalidOperationException("未完成经理审批,无法进行财务审批"); public override State? OnDeny() { return State.Afgekeurd; } }
5. 技术审批状态类(WaitForApprovalTechState)
public class WaitForApprovalTechState : FormState { public override State? OnSubmit() => throw new InvalidOperationException("已提交的表单无法重复提交"); public override State? OnManagerApproval() => throw new InvalidOperationException("已完成经理审批,无需重复操作"); public override State? OnTechApproval() { // 技术审批通过后转到财务审批 return State.WaitForApprovalFinance; } public override State? OnFinanceApproval() => throw new InvalidOperationException("未完成技术审批,无法进行财务审批"); public override State? OnDeny() { return State.Afgekeurd; } }
6. 财务审批完成状态类(WaitForApprovalFinanceState)
public class WaitForApprovalFinanceState : FormState { public override State? OnSubmit() => throw new InvalidOperationException("已提交的表单无法重复提交"); public override State? OnManagerApproval() => throw new InvalidOperationException("已完成经理审批,无需重复操作"); public override State? OnTechApproval() => throw new InvalidOperationException("已完成技术审批,无需重复操作"); public override State? OnFinanceApproval() { // 财务审批通过,表单完成 return State.Afgerond; } public override State? OnDeny() { return State.Afgekeurd; } }
改进后的优势
- 职责清晰:每个状态只处理自己能响应的动作,业务规则集中在对应的触发点
- 低耦合:新增状态时只需添加对应状态类和映射,无需修改其他状态类
- 可维护性:修改业务规则时,仅需找到对应动作方法修改,不会影响其他状态
- 可读性:从代码中能直观看到每个状态下允许的操作,以及操作后的状态转换逻辑
内容的提问来源于stack exchange,提问作者worst_developer69
相关产品推荐
相关产品推荐

