什么是有限状态机,一个系统可以分为很多个状态,通过一定的条件和规则可以触发状态与状态之间的变化。状态的变化过程会触发一系列行为。这种离散状态的流转机制及运行过程就叫有限状态机
应用案例1:截图工具
通过快捷键可以触发截图,在快捷键按下之前截图系统处于等待状态,在快捷键按下这个条件发生以后,系统进入截图区域选择状态,当按Esc后,系统又回到了等待状态,当鼠标按下并拖动时,系统处于拖动选区状态,当释放鼠标按键时进入等待确认状态,在此状态下,系统弹出选择菜单,可供你对选中的区域图片进行处理。当点击确认按钮后,系统对处理后的图片进行保存,退出截图,重新回到等待状态。
在整个过程中,状态是有限个,状态的变化需要有一个触发条件。触发前后,根据不同输入操作会产生状态分支。
我开发过一个工具就是按这个状态机模型设计的
应用案例2:TCP协议
有限状态机是一种用来进行对象行为建模的工具,其作用主要是描述对象在它的生命周期内所经历的状态序列,以及如何响应来自外界的各种事件。在计算机科学中,有限状态机被广泛用于建模应用行为、硬件电路系统设计、软件工程,编译器、网络协议、和计算与语言的研究。比如下图非常有名的TCP协议状态机。
应用案例三:解析格式化文本
为了简化问题,我们来看一个简单版本的格式文本解析问题
有一串文本,将文本中连续出现的空格变成一个,如果空格出现在字符串中则保持不变
例如 一串文本为
var i = a+b- c % s;
var s="Hello mmmmmm...";
压缩之后文本会变成
var i = a+b- c % s;
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:
var fsm = new StateMachine({
init: 'solid',
transitions: [
{ name: 'melt', from: 'solid', to: 'liquid' },
{ name: 'freeze', from: 'liquid', to: 'solid' },
{ name: 'vaporize', from: 'liquid', to: 'gas' },
{ name: 'condense', from: 'gas', to: 'liquid' }
],
methods: {
onMelt: function() { console.log('I melted') },
onFreeze: function() { console.log('I froze') },
onVaporize: function() { console.log('I vaporized') },
onCondense: function() { console.log('I condensed') }
}
});
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 states
is the current statefsm.can(t)
- return true if transitiont
can occur from the current statefsm.cannot(t)
- return true if transitiont
cannot occur from the current statefsm.transitions()
- return list of transitions that are allowed from the current statefsm.allTransitions()
- return list of all possible transitionsfsm.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动态状态转移指令触发已观测的状态转移方法
比如官方的物态变化为例子
var fsm = new StateMachine({
init: 'solid',
transitions: [
{ name: 'melt', from: 'solid', to: 'liquid' },
{ name: 'freeze', from: 'liquid', to: 'solid' },
{ name: 'vaporize', from: 'liquid', to: 'gas' },
{ name: 'condense', from: 'gas', to: 'liquid' },
{ name: 'goto', from: '*', to: ()=>['liquid', 'solid','gas'][Math.floor(Math.random()*3)] },
],
methods: {
onMelt: function() { console.log('I melted') },
onFreeze: function() { console.log('I froze') },
onVaporize: function() { console.log('I vaporized') },
onCondense: function() { console.log('I condensed') }
}
});
在状态机构造函数中增加了一个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转移方法
代码部分
修改之前的源码
修改后
系统同时监听额外三种事件
调用代码
//状态机工厂
StateMachine.factory(Analyse, {
init: "init",
transitions: [{
name: "create val",
from: "init",
to: "word"
},{
name: "create assign",
from: "word",
to: "assign"
}
,{
name: "create value",
from: "init",
to: "number"
}, {
name: 'goto',
from: '*',
to: function() {
var keystate = this.NextKey();
this.Codesplit.push(keystate);
return keystate.key;
},
dot: {
label: "ss"
}
}],
data: {
Codesplit: []
},
methods: {
onAfterInit:function(){
//解析对象初始化
this.AnalyseDom=new Squence();
this.DomPoint=this.AnalyseDom;
},
onCreateVal: function() {
if(this.DomPoint instanceof Squence){
var keyvalue = this.Codesplit.pop();
var val;
if(this.DomPoint.VarSpace.Exist(keyvalue.value)){
val=this.DomPoint.VarSpace.Find(keyvalue.value);
}else{
val = new Val(keyvalue.value);
this.DomPoint.VarSpace.Add(val);
};
this.DomPoint.appendChild(val);
this.DomPoint = val;
}
},
onCreateAssign: function() {
if(this.DomPoint instanceof Val){
var keyvalue = this.Codesplit.pop();
var assign = new Assign(this.DomPoint);
this.DomPoint.parent.replaceLastChild(assign);
this.DomPoint = assign;
}else if(this.DomPoint instanceof Value){
throw "无法对常量进行赋值";
}else if(this.DomPoint instanceof Method){
throw "无法对方法进行赋值";
}
},
onCreateValue( ){
if(1==1){
}
},
onAssign(){
if(1==1){
}
},
onBeforeTransition: function(lifecycle, arg1, arg2) {
console.log(lifecycle.transition);
// 'step'
console.log(lifecycle.from);
// 'A'
console.log(lifecycle.to);
lifecycle.dot = 'sssss';
}
},
plugins: [new StateMachineHistory()// <-- plugin enabled here
]
});
var FSM = new Analyse();
修改之后的代码具有很大的优势,比如不需要将所有的状态分支转换逻辑写在同一个方法内,系统根据状态变化,动态的调用已声明的响应回调方法,无需人工判断。
以上部分图片来源于网络,如有侵权,请联系删除。