降低业务复杂度-状态机范式

494 阅读5分钟

需求越多,代码数量越多,代码复杂度指数上升,某一个需求的迭代,都可能涉及很多地方的修改,维护成本过高,反而抑制了业务的迭代速度。

前端复杂度拆分主要包括:框架通用组件业务组件业务逻辑

当框架和通用组件建设完成后,能够承担的复杂度基本稳定了,后续无论是换框架或改善,都很难再有新突破。

所以,怎么样能把复杂度降下来。

换个角度,可以从业务共有的“业务逻辑”侧去进行突破。目前做业务侧提效的方案中,很少有从“业务逻辑”视角为出发点去做的,更多的是聚焦在场景化上的提效。

业务逻辑上,让业务复杂度提高的主要有以下两点:

  • 代码层面
    • 各种各样的业务状态导致的flag变量的剧增。
    • 各种判断业务状态的 if/else
  • 协作层面
    • 产品经理需求不合理,到了测试阶段才发现,又得重新改需求
    • 测试人员,前端不确定修改的代码会影响的范围,可能测试就不完全,或者全量回归测试,增加测试成本。
    • 其他前端开发人员,合作和交接项目时,会因为代码的复杂度太高而需要花费很长的时间去梳理逻辑。

解决方案:

1. 解决代码层面的问题:太多的flag变量,以及if/else嵌套判断,导致难以修改和扩展。可以用设计模式解决:状态模式

状态模式(多态特性和面向接口的体现)

主要解决的是;当控制一个对象状态转换的条件表达式过于复杂时的情况。 把状态的判断逻辑转移到表示不同状态的一系列类当中,减少互相间的依赖,可以把复杂的判断逻辑简化。 优点:将与特定状态相关的行为局部化,并且将不同状态的行为分割开来。 当一个对象的行为取决于它的状态,并且它必须在运行时刻根据状态改变它的行为时,可以考虑使用状态模式了。

状态机

全称:有限状态机,或称 有限状态自动机。是现实事物运行规则抽奖而成的一个数学模型。

有限状态机包含五个重要部分:

  • 初始状态值
  • 有限的一组状态
  • 有限的一组事件
  • 由事件驱动的一组状态转移关系
  • 有限的一组最终状态 简单来讲就三部分:
  • 状态state
  • 事件event
  • 转换transtion 拿到需求的时候,我们要分离出哪些是已知命题(state),哪些是条件event,哪些是结论transition。我们要通过已知命题和条件,推导出结论的过程。

例如Fetch API fetch(url).then().catch()

image.png

  • Idle状态只处理FETCH事件
  • Pending状态只处理RESOLVE和REJECT事件

做法:

  1. 将已知状态列出
  2. 将最终状态列出
  3. 将改变状态的事件列出
  4. 连接不同的状态,看状态改变的路径 可以做 可视化状态转换图,状态机的json描述等

2.解决协作层面的问题

解决方案:可视化。文档化。 单纯用状态机写代码,需求量多了,状态也会面临爆炸问题,所以,可以使用状态图解决方案。 状态图方案,前期的复杂度会比传统编码和状态机编码都复杂,但是后期需求量多了,复杂度也不会有明显上升。 状态图大概长这样,如图:

image.png

主要包括:

  • 状态
    • 原子状态
    • 复合状态
    • 条件状态
    • 最终状态
    • 历史状态
    • 初始状态
    • 并行状态
    • 伪/瞬间状态
  • 转换
    • 自动转换
    • 延迟转换
    • 自身转换
    • 内部转换
  • 操作
    • 自定义操作
    • 进入操作
    • 退出操作
    • 数据操作
    • 日志操作
  • 事件
    • 生成事件
    • 延迟时间
  • 条件
  • 数据
  • 调用

即使状态非常复杂,也可以通过状态图的模式进行聚合、分组、细化,还可以通过 Actor 模型进行划分,不会发生 “状态爆炸” 现象。

优势:

  • 比传统的编码方式,更容易理解
  • 基于行为建模,与视图解耦
    • 更容易改变行为:组件中的行为被提取到了状态机中,与 把行为和业务逻辑一起嵌入的组件相比,行为的更改相对容易。
    • 更容易的理解代码
    • 更容易测试
  • 构建状态图的过程必须探索所有的状态,也是让你具备业务全局视角的过程,它迫使你考虑所有可能发生的场景。
  • 基于状态图的代码比传统代码具有更少的bug数。
  • 有助于处理可能会被忽视的特殊情况
  • 随着复杂性的增加,状态图可以很好地扩展
  • 状态图是一个很好的交流工具 缺点:
  • 有学习成本
  • 前期时间成本较多
  • 项目规模不大时看不出效果

参考: 降低前端业务复杂度新视角:状态机范式