美文网首页IT修真院-前端
有限状态机是什么?

有限状态机是什么?

作者: 8778188a234f | 来源:发表于2017-09-10 12:38 被阅读0次

    一、背景介绍
    有限状态机是一种模型,用来模拟事物。事物一般有以下特点:
    1)可以用状态来描述事物,并且任一时刻,事物总是处于一种状态;
    2)事物拥有的状态总数是有限的;
    3)通过触发事物的某些行为,可以导致事物从一种状态过渡到另一种状态;
    4)事物状态变化是有规则的,A状态可以变换到B,B可以变换到C,A却不一定能变换到C;
    5)同一种行为,可以将事物从多种状态变成同种状态,但是不能从同种状态变成多种状态。
    二、知识剖析
    重点:状态和回调函数
    2.1回调函数callback
    英语原生含义:
    A callback is a function that is passed as an argument to another function and is executed after its parent function has completed.
    回调函数的几种写法:

    //异步请求回调函数
    $.get('ajax/test.html',function(data){
    $('.result').html(data);
    });
    
    //点击事件回调函数
    $('#target').click(function(){
    alert('Handle for .click() called.');
    });
    
    //数组中遍历每一项调用的回调函数
    this.tabs.forEach(function(tab,index){
    if(tab.selected){
    this.forcustab = this.tabs[index];
    }
    }.bind(this));
    
    //同步回调
    function getNodes(params,callback){
    var list = JSON.stringfy(params);
    typeof(callback)==='function' && callback(list)
    }
    getNodes('[1,2,3]',function(nodes)){
    //拿到nodes之后用它去做一些其他操作;
    };
    

    2.2状态机的写法
    比如这样一种情况,用对象来表现状态机模型:有一个菜单元素,鼠标悬停时,菜单显示;鼠标移开时,菜单隐藏。

    var fsm={
    // 当前状态
    currentState: 'hide',
    // 绑定事件
    initialize: function() {
    var self = this;
    self.on("hover", self.transition);
    },
    // 状态转换
    transition: function(event){
    switch(this.currentState) {
    case "hide":
    this.currentState = 'show';
    doSomething();//在此调用回调函数
    break;
    case "show":
    this.currentState = 'hide';
    doSomething();//在此调用回调函数
    break;
    default:
    console.log('Invalid State!');
    break;
    }
    },
    //callback
    callback1:function(){...},
    callback2:function(){...},
    ...
    };
    //执行
    fsm.initialize();
    ...
    //fsm.transition();
    ...
    

    2.3 Javascript Finite State Machine 函数库
    javascript-state-machine插件
    以下是引入函数库之后的写法,是关于交通信号灯的模型描述:

    //交通信号灯的模型描述:
    var fsm =new StateMachine({
    init: 'green',
    transitions: [
    { name: 'warn',  from: 'green',  to: 'yellow' },
    { name: 'stop', from: 'yellow', to: 'red' },
    { name: 'ready',  from: 'red',    to: 'yellow' },
    { name: 'go', from: 'yellow', to: 'green' }
    ],
    methods:{
    callback1:function(){...},
    callback2:function(){...},
    ...
    },
    error: function(){...}
    });
    

    init选项用来表示fsm对象的初始状态,transitions选项用来描述fsm对象所有状态的变化规则,每一种变化规则对应一种行为。create方法为实例的每一种行为都添加了一个方法,调用这个方法就相当于触发对象的某种行为,当对象行为发生时,对象的状态就可以发生变化。如以上例子创建的实例将拥有如下行为方法:
    fsm.warn() : 调用该方法,实例状态将从'green'变为'yellow'
    fsm.stop() : 调用该方法,实例状态将从'yellow'变为'red'
    fsm.ready() : 调用该方法,实例状态将从'red'变为'yellow'
    fsm.go() : 调用该方法,实例状态将从'yellow'变为'green'
    这些方法是StateMachine根据create时配置的transitions规则自动创建的,方法名跟transitions规则里面的name属性对应,transitions规则里面有几个不重复的name,就会添加几个行为方法。同时为了方便使用,它还添加了如下成员来判断和控制实例的状态和行为:
    fsm.state - 返回实例当前的状态
    fsm.is(state) - 如果传入的state是实例当前状态就返回true
    fsm.can(eventName) - 如果传入的eventName在实例当前状态能够被触发就返回true
    fsm.cannot(eventName) - 如果传入的eventName在实例当前状态不能被触发就返回true
    fsm.transitions() - 以数组的形式返回实例当前状态下能够被触发的行为列表
    Javascript Finite State Machine允许为每个事件指定两个回调函数,以warn事件为例: onBeforeWarn:在warn事件发生之前触发 onAfterWarn(可简写成onWarn) :在warn事件发生之后触发。 同时,它也允许为每个状态指定两个回调函数,以green状态为例: onLeaveGreen :在离开green状态时触发 onEnterGreen(可简写成onGreen) :在进入green状态时触发。
    假定warn事件使得状态从green变为yellow,上面四类回调函数的发生顺序为:
    onBeforeWarn → onLeaveGreen → onEnterYellow → onAfterWarn。
    还为所有的事件和状态指定通用的回调函数:
    onBeforeEvent :任一事件发生之前触发
    onLeaveState :离开任一状态时触发
    onEnterState :进入任一状态时触发
    onAfterEvent :任一事件结束后触发
    三、常见问题
    如何使用有限状态机?
    四、解决方案
    将需求模型化,划分状态和相应的触发事件与动作,利用这些构建类,控制执行。
    五、编码实战
    杀人游戏实例
    以下是一个小demo,仅作为一个思路提供给大家,并非完整代码。

    var state=storage["state"]?storage["state"]:'none';
    function demo(state) {
        var fsm=new StateMachine({
            init:state,
            transitions:[
                {name:'kill', from:'none', to:'killed'},
                {name:'tosay',from:'killed',to:'testament'},
                {name:'todiscuss',from:'testament',to:'discussing'},
                {name:'tovote',from:'discussing',to:'voting'},
                {name:'toclear',from:'voting',to:'none'}
            ],
            methods:{
                onBeforetosay:function (lifecycle) {
                    // 按钮变色
                    $("#night-steps"+count.toString()+
                    " .announcement").text(decedent[decedent.length-1].index+"号被杀手杀死,身份是"+
                        decedent[decedent.length-1].identity).attr("class","text-style");
                    $(".kill").children(".triangle").css("border-right-color","#83b09a");
                    $(".kill").unbind("click").css("background-color","#83b09a");
                },
                ontosay:function (lifecycle) {
                    alert("请死者亮明身份并发表遗言");
                    // 按钮变色
                    $(".last-words").unbind("click").css("background-color","#83b09a");
                    $(".last-words").children(".triangle").css("border-right-color","#83b09a");
                },
                ontodiscuss:function (lifecycle) {
                    alert("玩家依次发言讨论");
                    // 按钮变色
                    $(".speak").unbind("click").css("background-color","#83b09a");
                    $(".speak").children(".triangle").css("border-right-color","#83b09a");
                },
                ontovote:function () {
                    storage.setItem("state",fsm.state);
                    window.location.href="kill-vote.html?";
    
                }
            }
        });
        $(".kill").click(function () {
            fsm.kill();
            storage.setItem("state",fsm.state);
            window.location.href="kill-vote.html?";
        });
        $(".lastwords").click(function () {
            if(fsm.state==="killed"){
                fsm.tosay();
                storage.setItem("state",fsm.state);
            }else{
                alert("请先杀人");
            }
    
        });
        $(".speak").click(function () {
            if(fsm.state==="testament"){
                fsm.todiscuss();
                storage.setItem("state",fsm.state);
            }else if(fsm.state==="killed"){
                alert("请先发表遗言")
            }else{
                alert("请先杀人")
            }
    
        });
        $(".vote").click(function () {
            if(fsm.state==="discussing"){
                fsm.tovote();
                storage.setItem("state",fsm.state);
            }else if(fsm.state==="killed"){
                alert("请先发表遗言")
            }else if(fsm.state==="testament"){
                alert("请玩家先发言讨论")
            }else{
                alert("请先杀人")
            }
    
        });
        switch(state){
            case 'none':
                storage.setItem("state",state);
                return fsm;
                break;
            case 'killed':
                fsm.onBeforetosay();
                return fsm;
                break;
        }
    }
    demo(state);
    
    

    六、扩展思考
    以下是Javascript Finite State Machine 函数库上的一个demo,信号灯实例
    代码演示:demo
    七、更多讨论
    讨论一、同步调用、回调和异步调用有什么区别?
    讨论二、有限状态机有哪些优点,与普通的if判断语句相比呢?
    讨论三、有限状态机还有哪些应用场景?
    八、参考文献
    参考一:阮一峰:Javascript与有限状态机
    参考二:流云诸葛:用有限状态机的思路定义组件
    参考三:Cayley的世界:关于回调函数callback

    相关文章

      网友评论

        本文标题:有限状态机是什么?

        本文链接:https://www.haomeiwen.com/subject/mpizjxtx.html