首页 » 小蓝 » 从一个页面开关, 说说有限状态机

从一个页面开关, 说说有限状态机

故事起源于最近看一位朋友在实现一个 播放/暂停 按钮时, 一些思路上的碰撞,
于是整理出本文, 讲解一下如何用 有限状态机 使代码更简洁可靠.

我们想实现上面这样的按钮交互, 先看看原版实现:

$('#botton').on('click', function(){
    if ( $(this).text() === 'OFF' ){
        //把按钮的文案改变一下
        $(this).text('ON');
        //开始播放
        $('#player').start();
    } else {
        $(this).text('OFF');
        $('#player').stop();
    }
});

这是一个基本的实现方式, 也是一个最简单的方式.

不过,

按照这个方式来做有几个不太优雅的地方:

  • 如果点击按钮之后, 需要做的操作增加, 会让if / else里面的代码越来越臃肿.
  • 如果按钮的变化不止 on/off两种, 我们可能需要些一堆 else if或者用switch / case

针对以上状况, 我提议试试使用有限状态机来解决问题

先看代码:

var fsm = (function(){
    //初始状态
    var status = 1; 
    
    //状态对应的操作
    var mapping = {
        '1': {
            text:'ON',
            action: $('#player').start
        },
        '-1': {
            text:'OFF',
            action: $('#player').stop
        },
    };
    
    return function(btn){
        //通过 *-1 实现status从 1/-1 切换
        status *= -1;
        btn.text( mapping[status].text );
        var fn = mapping[status].action;
        fn();
    }
})();

$('#botton').click(function(){
    fsm(this);
});

阅读上面代码能发现, 对botton的点击事件处理, 只需要调用fsm函数即可, 内部的变化和操作, 都不需要暴露出来.

fsm内部, 通过mapping来定义和限制行为, 唯一能够改变的只有status,

这样的好处在于, 能够避免在编码过程中人为的错误, 因为事件响应部分只能有限的操作状态机的status,而不是直接参与botton的行为与表现.

同时, 功能的扩展, 状态的增减, 都只需要在mapping里面定义好, 非常利于扩展与维护.