当前位置:   article > 正文

有限状态机(Finite State Machine)及一种JS实现框架的介绍及改进_js 有限状态机

js 有限状态机

什么是有限状态机,一个系统可以分为很多个状态,通过一定的条件和规则可以触发状态与状态之间的变化。状态的变化过程会触发一系列行为。这种离散状态的流转机制及运行过程就叫有限状态机

应用案例1:截图工具

通过快捷键可以触发截图,在快捷键按下之前截图系统处于等待状态,在快捷键按下这个条件发生以后,系统进入截图区域选择状态,当按Esc后,系统又回到了等待状态,当鼠标按下并拖动时,系统处于拖动选区状态,当释放鼠标按键时进入等待确认状态,在此状态下,系统弹出选择菜单,可供你对选中的区域图片进行处理。当点击确认按钮后,系统对处理后的图片进行保存,退出截图,重新回到等待状态。

在整个过程中,状态是有限个,状态的变化需要有一个触发条件。触发前后,根据不同输入操作会产生状态分支。

我开发过一个工具就是按这个状态机模型设计的

应用案例2:TCP协议

有限状态机是一种用来进行对象行为建模的工具,其作用主要是描述对象在它的生命周期内所经历的状态序列,以及如何响应来自外界的各种事件。在计算机科学中,有限状态机被广泛用于建模应用行为、硬件电路系统设计、软件工程,编译器、网络协议、和计算与语言的研究。比如下图非常有名的TCP协议状态机。

应用案例三:解析格式化文本

为了简化问题,我们来看一个简单版本的格式文本解析问题

有一串文本,将文本中连续出现的空格变成一个,如果空格出现在字符串中则保持不变

例如 一串文本为

  1. var i = a+b- c % s;
  2. var s="Hello mmmmmm...";

压缩之后文本会变成 

  1. var i = a+b- c % s;
  2. var s="Hello mmmmmm...";

 这个出来看似不是很复杂,但现实的情况可能比这个复杂的多

状态图类似于这样:

这里写图片描述

 复杂的状态机可能类似于这种

工作流状态机

游戏系统中的状态机

本文最后介绍状态机模型的一种JS实现框架

源码:

GitHub - jakesgordon/javascript-state-machine: A javascript finite state machine library

该JS框架实现了状态的的构建,状态转移命令,命令执行先后触发事件回调,状态变化前后触发回调,状态跳转,状态历史记录回溯,多实例状态共享数据,多实例状态机,状态机可视化,状态转移输入参数,动态状态,动态创建销毁回调

为什么要做这样一个框架,因为很多复杂系统都涉及状态机,使用框架可以最大程度减少重复代码,减少状态和方法的构造,增加对状态机系统的理解和把握。

下面介绍它简单的使用案例

A library for finite state machines.

A state machine can be constructed using:

  1. var fsm = new StateMachine({
  2. init: 'solid',
  3. transitions: [
  4. { name: 'melt', from: 'solid', to: 'liquid' },
  5. { name: 'freeze', from: 'liquid', to: 'solid' },
  6. { name: 'vaporize', from: 'liquid', to: 'gas' },
  7. { name: 'condense', from: 'gas', to: 'liquid' }
  8. ],
  9. methods: {
  10. onMelt: function() { console.log('I melted') },
  11. onFreeze: function() { console.log('I froze') },
  12. onVaporize: function() { console.log('I vaporized') },
  13. onCondense: function() { console.log('I condensed') }
  14. }
  15. });

which creates an object with a current state property:

  • fsm.state

methods to transition to a different state:

  • fsm.melt()
  • fsm.freeze()
  • fsm.vaporize()
  • fsm.condense()

observer methods called automatically during the lifecycle of a transition:

  • onMelt()
  • onFreeze()
  • onVaporize()
  • onCondense()

 along with the following helper methods:

  • fsm.is(s) - return true if state s is the current state
  • fsm.can(t) - return true if transition t can occur from the current state
  • fsm.cannot(t) - return true if transition t cannot occur from the current state
  • fsm.transitions() - return list of transitions that are allowed from the current state
  • fsm.allTransitions() - return list of all possible transitions
  • fsm.allStates() - return list of all possible states

Terminology

A state machine consists of a set of States

  • solid
  • liquid
  • gas

A state machine changes state by using Transition

  • melt
  • freeze
  • vaporize
  • condense

A state machine can perform actions during a transition by observing LifeCycle Even

  • onBeforeMelt
  • onAfterMelt
  • onLeaveSolid
  • onEnterLiquid
  • ...

A state machine can also have arbitrary Data and Methods.

Multiple instances of a state machine can be created using a State Machine Factory.

功能增强

使用GOTO动态状态转移指令触发已观测的状态转移方法

比如官方的物态变化为例子

  1. var fsm = new StateMachine({
  2. init: 'solid',
  3. transitions: [
  4. { name: 'melt', from: 'solid', to: 'liquid' },
  5. { name: 'freeze', from: 'liquid', to: 'solid' },
  6. { name: 'vaporize', from: 'liquid', to: 'gas' },
  7. { name: 'condense', from: 'gas', to: 'liquid' },
  8. { name: 'goto', from: '*', to: ()=>['liquid', 'solid','gas'][Math.floor(Math.random()*3)] },
  9. ],
  10. methods: {
  11. onMelt: function() { console.log('I melted') },
  12. onFreeze: function() { console.log('I froze') },
  13. onVaporize: function() { console.log('I vaporized') },
  14. onCondense: function() { console.log('I condensed') }
  15. }
  16. });

在状态机构造函数中增加了一个Goto状态,当触发Goto指令时,随机跳转到任何一个状态(跟薛定谔的猫很相似,和上帝之手一样,黑线代表坍缩到真实物理世界能被人理解的转移过程,红线代表随机游走的宇宙本质)

{ name: 'goto', from: '*',    to: s=>['liquid', 'solid','gas'][Math.floor(Math.random()*3)] }

状态转移图如下

 其中每个椭圆代表状态机的状态,椭圆之间的连线代表那些状态可以相互转移,以及转移的方法,goto是一种特殊的转移指令,当goto指令运行后,会触发转移状态指令运行,

加入,当前状态是gas,通过goto指令操作随机跳转到liquid状态,此时,除了触发转移变化事件,还会触发onCondese事件。

修改前生命周期事件执行的先后顺序为

  • onBeforeTransition - fired before any transition

  • onBefore<TRANSITION> - fired before a specific TRANSITION

  • onLeaveState - fired when leaving any state

  • onLeave<STATE> - fired when leaving a specific STATE

  • onTransition - fired during any transition

  • onEnterState - fired when entering any state

  • onEnter<STATE> - fired when entering a specific STATE

  • on<STATE> - convenience shorthand for onEnter<STATE>

  • onAfterTransition - fired after any transition

  • onAfter<TRANSITION> - fired after a specific TRANSITION

  • on<TRANSITION> - convenience shorthand for onAfter<TRANSITION>

修改后,比原来多监测了3种事件:

  • onBeforeTransition - fired before any transition

  • onBeforeGoto - fired before Goto

  • onBefore<Transition>-触发与Goto前后状态相同的Before转移方法

  • onLeaveState -在Goto转移指令之前的任何状态变化后执行

  • onLeave<STATE> - 在Goto转移指令之前的指定状态变化后执行

  • onTransition - 任何转移指令触发时执行

  • onEnterState - 转移至任何状态后执行

  • onEnter<STATE>/on<STATE> - 转移至指定状态后执行

  • onAfterTransition - 任何转移指令触发后执行

  • onAfterGoto/onGoto - fired after Goto

  • onAfter<Transition>/on<Transition>-触发与Goto前后状态相同的After转移方法

代码部分

修改之前的源码

修改后

系统同时监听额外三种事件

调用代码

  1. //状态机工厂
  2. StateMachine.factory(Analyse, {
  3. init: "init",
  4. transitions: [{
  5. name: "create val",
  6. from: "init",
  7. to: "word"
  8. },{
  9. name: "create assign",
  10. from: "word",
  11. to: "assign"
  12. }
  13. ,{
  14. name: "create value",
  15. from: "init",
  16. to: "number"
  17. }, {
  18. name: 'goto',
  19. from: '*',
  20. to: function() {
  21. var keystate = this.NextKey();
  22. this.Codesplit.push(keystate);
  23. return keystate.key;
  24. },
  25. dot: {
  26. label: "ss"
  27. }
  28. }],
  29. data: {
  30. Codesplit: []
  31. },
  32. methods: {
  33. onAfterInit:function(){
  34. //解析对象初始化
  35. this.AnalyseDom=new Squence();
  36. this.DomPoint=this.AnalyseDom;
  37. },
  38. onCreateVal: function() {
  39. if(this.DomPoint instanceof Squence){
  40. var keyvalue = this.Codesplit.pop();
  41. var val;
  42. if(this.DomPoint.VarSpace.Exist(keyvalue.value)){
  43. val=this.DomPoint.VarSpace.Find(keyvalue.value);
  44. }else{
  45. val = new Val(keyvalue.value);
  46. this.DomPoint.VarSpace.Add(val);
  47. };
  48. this.DomPoint.appendChild(val);
  49. this.DomPoint = val;
  50. }
  51. },
  52. onCreateAssign: function() {
  53. if(this.DomPoint instanceof Val){
  54. var keyvalue = this.Codesplit.pop();
  55. var assign = new Assign(this.DomPoint);
  56. this.DomPoint.parent.replaceLastChild(assign);
  57. this.DomPoint = assign;
  58. }else if(this.DomPoint instanceof Value){
  59. throw "无法对常量进行赋值";
  60. }else if(this.DomPoint instanceof Method){
  61. throw "无法对方法进行赋值";
  62. }
  63. },
  64. onCreateValue( ){
  65. if(1==1){
  66. }
  67. },
  68. onAssign(){
  69. if(1==1){
  70. }
  71. },
  72. onBeforeTransition: function(lifecycle, arg1, arg2) {
  73. console.log(lifecycle.transition);
  74. // 'step'
  75. console.log(lifecycle.from);
  76. // 'A'
  77. console.log(lifecycle.to);
  78. lifecycle.dot = 'sssss';
  79. }
  80. },
  81. plugins: [new StateMachineHistory()// <-- plugin enabled here
  82. ]
  83. });
  84. var FSM = new Analyse();

修改之后的代码具有很大的优势,比如不需要将所有的状态分支转换逻辑写在同一个方法内,系统根据状态变化,动态的调用已声明的响应回调方法,无需人工判断。

以上部分图片来源于网络,如有侵权,请联系删除。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/2023面试高手/article/detail/696600
推荐阅读
相关标签
  

闽ICP备14008679号