01/25/2018 从一个页面开关, 说说有限状态机

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

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

$('#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里面定义好, 非常利于扩展与维护.

侧栏导航