started: ,
aborted: ,
launched:
}
系统中四个(控制)状态分别对应于Model中如下的值:
ready = {counter: 10, started: false, aborted: false, launched: false }
counting = {counter: [0..10], started: true, aborted: false, launched: false }
launched = {counter: 0, started: true, aborted: false, launched: true}
aborted = {counter: [0..10], started: true, aborted: true, launched: false}
这个Model是由系统的所有属性及其可能的值所指定的,状态则指定了所启用的Action,它会给定一组值 。这种类型的业务逻辑必须要在某个地方进行实现 。我们不能指望用户能够知道哪个Action是否可行 。在这方面,没有其他的方式 。不过,这种类型的业务逻辑很难编写、调试和维护,在没有语义对其进行描述时,更是如此,比如在MVC中就是这样 。
让我们为火箭发射的样例编写一些代码 。从TLA+角度来讲,next-action断言在逻辑上会跟在状态渲染之后 。当前状态呈现之后,下一步就是执行next-action断言,如果存在的话,将会计算并执行下一个Action,这个Action会将其数据交给Model,Model将会初始化新状态的表述,以此类推 。

文章插图
图5:火箭发射器的实现
需要注意的是,在客户端/服务器架构下,当自动Action触发之后,我们可能需要使用像WebSocket这样的协议(或者在WebSocket不可用的时候,使用轮询机制)来正确地渲染状态表述 。
我曾经使用Java和JavaScript编写过一个很轻量级的开源库,它使用TLA+特有的语义来构造状态对象,并提供了样例,这些样例使用WebSocket、轮询和队列实现浏览器/服务器交互 。在火箭发射器的样例中可以看到,我们并非必须要使用那个库 。一旦理解了如何编写,状态实现的编码相对来讲是很容易的 。
新模式——SAM模式
对于要引入的新模式来说,我相信我们已经具备了所有的元素,这个新模式作为MVC的替代者,名为SAM模式(状态-行为-模型,State-Action-Model),它具有反应型和函数式的特性,灵感来源于React.js和TLA+ 。
SAM模式可以通过如下的表达式来进行描述:
V = S( vm( M.present( A(data) ) ), nap(M))
它表明在应用一个Action A之后,View V可以计算得出,Action会作为Model的纯函数 。
在SAM中,A(Action)、vm(视图-模型,view-model)、nap(next-action断言)以及S(状态表述)必须都是纯函数 。在SAM中,我们通常所说的“状态”(系统中属性的值)要完全局限于Model之中,改变这些值的逻辑在Model本身之外是不可见的 。
随便提一下,next-action断言,即nap()是一个回调,它会在状态表述创建完成,并渲染给用户时调用 。

文章插图
图6:状态-行为-模型(SAM)模式
模式本身是独立于任何协议的(可以不费什么力气就能在HTTP上实现)和客户端/服务器拓扑结构的 。
SAM并不意味着我们必须要使用状态机的语义来获取View的内容 。如果Action是由View触发的,那next-action断言就是一个空函数 。不过,这可能是一个很好的实践,它清晰暴露了底层状态机的控制状态,因为根据(控制)状态的不同,View看起来可能也是不同的 。
另一方面,如果你的状态机涉及到自动化的Action,那么Action和Model都不可能做到纯粹的不包含next-action断言:有些Action将会变得有状态,或者Model必须要触发Action,而这本来并不是它的角色 。顺便提一下,也许并不那么直观,状态对象并没有持有任何的“状态”,它同样也是纯函数,它会渲染View并计算next-action断言,这两者都来源于Model的属性值 。
这种新模式的好处在于,它清晰地将CRUD操作从Action中分离了出来 。Model负责它的持久化,将会通过CRUD操作来实现,通过View是无法进行访问的 。尤其是,View永远不会处于“获取”数据的位置,View所能做的唯一的事情就是请求系统中当前的状态表述并通过触发Action初始化一个反应型流程 。
Action仅仅代表了一种具有权限的通道,以此来建议Model该怎样进行变更 。它们本身(在Model方面)并没有什么副作用 。如果必要的话,Action会调用第三方的API(同样,对Model没有副作用),比如说,修改地址的Action可能会希望调用地址校验服务,并将服务返回的地址提交到Model中 。
如下就是“修改地址”Action该如何进行实现,它会调用地址校验的API:
推荐阅读
- 为什么说要用DDD替代CRUD来设计API
- 优化Python代码的4种方法
- 聪明的女人结婚不领证 女方为什么要先办婚礼再领证
- 李想|把游戏机统统搬到车上!李想:我要在理想L9上玩真正的3A大作
- 梦见牛追我但躲开了 梦见被牛追到处躲藏
- 肌肉松弛剂介绍
- 祁门红茶的历史典故
- 吸盘挂钩时间长了为什么会掉 粘钩吸盘老脱落怎么办
- 清朝出现过僵尸吗 僵尸为什么穿着清朝的衣服
- 为什么会有绿帽子的说法 为什么有绿帽子这个说法
