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

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

应用案例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 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动态状态转移指令触发已观测的状态转移方法

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

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();

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

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

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值