需求越多,代码数量越多,代码复杂度指数上升,某一个需求的迭代,都可能涉及很多地方的修改,维护成本过高,反而抑制了业务的迭代速度。
前端复杂度拆分主要包括:框架、通用组件、业务组件、业务逻辑。
当框架和通用组件建设完成后,能够承担的复杂度基本稳定了,后续无论是换框架或改善,都很难再有新突破。
所以,怎么样能把复杂度降下来。
换个角度,可以从业务共有的“业务逻辑”侧去进行突破。目前做业务侧提效的方案中,很少有从“业务逻辑”视角为出发点去做的,更多的是聚焦在场景化上的提效。
业务逻辑上,让业务复杂度提高的主要有以下两点:
- 代码层面
- 各种各样的业务状态导致的
flag变量的剧增。 - 各种判断业务状态的
if/else
- 各种各样的业务状态导致的
- 协作层面
- 产品经理需求不合理,到了测试阶段才发现,又得重新改需求
- 测试人员,前端不确定修改的代码会影响的范围,可能测试就不完全,或者全量回归测试,增加测试成本。
- 其他前端开发人员,合作和交接项目时,会因为代码的复杂度太高而需要花费很长的时间去梳理逻辑。
解决方案:
1. 解决代码层面的问题:太多的flag变量,以及if/else嵌套判断,导致难以修改和扩展。可以用设计模式解决:状态模式
状态模式(多态特性和面向接口的体现)
主要解决的是;当控制一个对象状态转换的条件表达式过于复杂时的情况。 把状态的判断逻辑转移到表示不同状态的一系列类当中,减少互相间的依赖,可以把复杂的判断逻辑简化。 优点:将与特定状态相关的行为局部化,并且将不同状态的行为分割开来。 当一个对象的行为取决于它的状态,并且它必须在运行时刻根据状态改变它的行为时,可以考虑使用状态模式了。
状态机
全称:有限状态机,或称 有限状态自动机。是现实事物运行规则抽奖而成的一个数学模型。
有限状态机包含五个重要部分:
- 初始状态值
- 有限的一组状态
- 有限的一组事件
- 由事件驱动的一组状态转移关系
- 有限的一组最终状态 简单来讲就三部分:
- 状态state
- 事件event
- 转换transtion 拿到需求的时候,我们要分离出哪些是已知命题(state),哪些是条件event,哪些是结论transition。我们要通过已知命题和条件,推导出结论的过程。
例如Fetch API fetch(url).then().catch()
- Idle状态只处理FETCH事件
- Pending状态只处理RESOLVE和REJECT事件
做法:
- 将已知状态列出
- 将最终状态列出
- 将改变状态的事件列出
- 连接不同的状态,看状态改变的路径 可以做 可视化状态转换图,状态机的json描述等
2.解决协作层面的问题
解决方案:可视化。文档化。 单纯用状态机写代码,需求量多了,状态也会面临爆炸问题,所以,可以使用状态图解决方案。 状态图方案,前期的复杂度会比传统编码和状态机编码都复杂,但是后期需求量多了,复杂度也不会有明显上升。 状态图大概长这样,如图:
主要包括:
- 状态
-
- 原子状态
- 复合状态
- 条件状态
- 最终状态
- 历史状态
- 初始状态
- 并行状态
- 伪/瞬间状态
- 转换
-
- 自动转换
- 延迟转换
- 自身转换
- 内部转换
- 操作
-
- 自定义操作
- 进入操作
- 退出操作
- 数据操作
- 日志操作
- 事件
-
- 生成事件
- 延迟时间
- 条件
- 数据
- 调用
即使状态非常复杂,也可以通过状态图的模式进行聚合、分组、细化,还可以通过 Actor 模型进行划分,不会发生 “状态爆炸” 现象。
优势:
- 比传统的编码方式,更容易理解
- 基于行为建模,与视图解耦
- 更容易改变行为:组件中的行为被提取到了状态机中,与 把行为和业务逻辑一起嵌入的组件相比,行为的更改相对容易。
- 更容易的理解代码
- 更容易测试
- 构建状态图的过程必须探索所有的状态,也是让你具备业务全局视角的过程,它迫使你考虑所有可能发生的场景。
- 基于状态图的代码比传统代码具有更少的bug数。
- 有助于处理可能会被忽视的特殊情况
- 随着复杂性的增加,状态图可以很好地扩展
- 状态图是一个很好的交流工具 缺点:
- 有学习成本
- 前期时间成本较多
- 项目规模不大时看不出效果