You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
在真实世界中,状态无处不在。红绿灯的颜色、人的喜怒哀乐、公司 OA 系统的审批流程等等都属于状态。
在 Web 开发中,状态也无处不在,选项卡的切换、loading 状态、开关按钮等等都有状态之间的切换。实际上我们前端开发中也是在和不同的状态打交道,知名前端框架 React 中就有 state 的概念。
1. 前言
在真实世界中,状态无处不在。红绿灯的颜色、人的喜怒哀乐、公司 OA 系统的审批流程等等都属于状态。
在 Web 开发中,状态也无处不在,选项卡的切换、loading 状态、开关按钮等等都有状态之间的切换。实际上我们前端开发中也是在和不同的状态打交道,知名前端框架 React 中就有 state 的概念。
2. 状态模式
2.1 红绿灯的例子
先来看个红绿灯的例子,红绿灯一般会有三种状态,红灯表示禁止通行,绿灯表示准许通行,黄灯表示警示,这三种状态对应了不同的行为。
如果是用传统的方式来编写代码,那就是像下面这样,需要用一堆
if...else
或者switch...case
来判断状态。这段代码主要就有下面这三个问题:
switch
方法。2.2 状态模式重写红绿灯
状态模式是一种行为型模式,对象可以根据状态的改变来改变自己的行为。
一般来说,我们都是封装对象的行为,而非对象的状态。而在状态模式里面,就是把事物的每种状态都封装成独立的类,和状态有关的行为都在类的内部。在切换状态的时候,只需要在上下文中把请求委托给当前状态对象就行了。
以上面的代码为例,我们不需要在 switch 方法中进行判断、转换,只要把对应的状态转换关系封装在不同的状态类中就行了。
这里的 light 即上下文,在每次切换状态的时候它不会进行实质性的操作,而是委托给状态类来执行。
从上述代码中就可以看出状态模式的优势,
if...else
已经被消除了,我们不需要通过if...else
来控制状态之间的切换。如果以后增加新的状态,只需要增加新的状态类就行了。状态模式的缺点就是会在系统中创建大量的类,增加了系统的负担。同时逻辑分散在不同的状态类中,我们无法清晰地看出整个状态机的逻辑。
3. 状态机
3.1 什么是有限状态机?
有限状态机(Finite-state machine)是编译原理中的一个概念,表示有限个状态以及在这些状态之间的转移和动作等行为的数学模型。
状态机这个词可能对于大家来说比较陌生,但在 Promise、generator 中都有状态机的影子。
有限状态机一般具有以下特点:
1)可以用状态来描述事物,在任何时刻,事物总是处于一种状态;
2)状态总数是有限的;
3)通过触发某种条件,事物可以从一种状态转变到另一种状态;
4)状态变化按照规则来,可以从 A 到 B,从 B 到 C,但不一定能从 A 到 C。
5)同一个条件,可以将事物从多个状态变成同一个状态,却不能从一种状态变成多种状态。
如果用公式来描述一下这个转换,那就是:
如果你有写过 React/Redux,会发现和他们的原理是如此相似,甚至 React 中也是用
state/setState
来命名状态/修改状态。3.2 javascript-state-machine
在 web 开发中,我们也经常会遇到状态切换的场景,比如有个灯泡,点击打开按钮就会发亮,点击关闭按钮就会关闭;常用的菜单栏,鼠标移上去会展开,鼠标移开会隐藏等等。
如果结合状态模式来实现,那就是下面这样:
上面这段代码虽然实现了功能,但还存在这么几个问题:
前面我们讲过表驱动,如果使用表来配置转换之间的转换关系呢?将状态的转换封装起来,我们只需要实现业务逻辑就行了。
现在已经有一个库帮你实现了这个功能。javascript-state-machine 是一个经典的有限状态机库,它的用法也比较接近表驱动。
我们可以定义初始状态、转换规则,以及其转换后的回调函数。
javascript-state-machine 在使用的时候,常常需要生成一个实例,将初始状态、转换规则等属性当做配置传进去。
name
是触发这个转换规则时的条件方法。除了这些常规的 API 之外,javascript-state-machine 还提供了一系列的生命周期的钩子函数。
onBeforeShow
。onAfterShow
。onLeaveOff
。onEnterOff
。看到这里,你有没有觉得和 Vue/Mobx 很像?Vue 和 Mobx 都实现了例如 watch/reaction 这种功能,允许你监听某个属性的变化,自动去执行某些操作。
如果将 methods 改成 actions,也许你会发现这个理念和 redux 非常相似。
在我看来,不管是 react 还是 redux 都有状态机的思想在里面。在 react 中,
local state
代表组件内部的状态,粒度比较细,常和一些 UI 上面的交互有关。而在 redux 里面,
global state
是一个更复杂的状态机,它常常用来管理一些全局的状态。3.3 业务中的有限状态机
如果你有做过下拉分页列表的需求,那么一定会经常遇到这些问题。
整个流程的状态图如下:
可以看到,在代码中管理这么多状态本来就已经很头疼了,还要考虑处理各种转换的场景。
状态转移图:
|当前条件/状态| loading | error | finish |
| :-----| ----: | :----: |
|弱网、超时|error||
|重新加载||loading|
|最后一页|finish||
这么多状态之间的转换,如果用状态机来实现,是不是会清晰很多呢?
经过 javascript-state-machine 重构后的代码,虽然在这个例子中看起来代码量增加了,但这里将 DOM 操作和业务逻辑解耦开了,我们都不需要关心每次 DOM 的隐藏和展示,这在复杂业务下的可维护性会大大提高。
推荐阅读
The text was updated successfully, but these errors were encountered: