状态模式
工作中,常常使用到状态机,如何才能把一个流程确定的状态机代码编写好,恰恰是状态模式要解决的问题。
假设目前有一个场景,一个按钮有A B C三种状态,状态切换的流程为 A -> B -> C -> A...
按钮在不同的状态下面,按钮执行不同的点击动作,如何去编写者一块代码?
一般的编写思路为
// html <button id="button">click</button>
var state = {A: 1, B: 2, C: 3};
var currentState = state.A; //default
var button = document.getElementById("button");
button.onclick = function() {
if (currentState === state.A) {
console.log('operating in state A');
currentState = state.B;
} else if (currentState === state.B) {
console.log('operating in state B');
currentState = state.C;
} else if (currentState === state.C) {
console.log('operating in state C');
currentState = state.A;
} else {
throw new Error("can not find the state");
}
}
上面的写法有什么问题吗?当然是有的,多个状态全部用if else判断,如果新增状态的话,是不是要修改这个onclick函数,而且如果在不同的状态下,做的逻辑比较复杂的话,是不是onclick函数会显得臃肿许多。
有没有更好的办法呢?
- 使用状态模式,状态模式要解决的问题是,在当前的状态下面,就直接做这个动作,而不是判断是否处于这个状态下面,去做这个动作,类似于多态,不管你当前是啥,反正我直接调用你这个动作执行的方法就行了,
- 在状态模式使用的过程中,我们一般使用委托,把当前需要做的动作,直接委托到当前这个状态的对象的动作中来,无需判断当前是处于哪个状态,所以状态对象提供的动作接口必须是一致的
状态模式改进版
// 为了向上提供一致的接口,每个状态执行的动作函数名称必须一致
var FSM = {
A: {
//我只是关心A状态下面 会做什么
handler: function() {
console.log('operating in state A');
currentState = FSM.B;
}
},
B: {
handler: function() {
console.log('operating in state B');
currentState = FSM.C;
}
},
C: {
handler: function() {
console.log('operating in state C');
currentState = FSM.A;
}
}
}
var currentState = FSM.A; //default
var button = document.getElementById("button");
button.onclick = function() {
currentState.handler.call(this);
//把当前的onclick委托给 现在状态的handler
//这里我不关心当前这个状态是什么,我只是关心当前这个状态会发生什么,所以去除了if else
}
完整代码:
<!DOCTYPE html>
<html>
<head></head>
<body>
<button id="button">click</button>
<script type="text/javascript">
// 为了向上提供一致的接口,每个状态执行的动作函数名称必须一致
var FSM = {
A: {
//我只是关心A状态下面 会做什么
handler: function() {
console.log('operating in state A');
currentState = FSM.B;
}
},
B: {
handler: function() {
console.log('operating in state B');
currentState = FSM.C;
}
},
C: {
handler: function() {
console.log('operating in state C');
currentState = FSM.A;
}
}
}
var currentState = FSM.A; //default
var button = document.getElementById("button");
button.onclick = function() {
currentState.handler.call(this);
//用call的目的是 如果状态里面想要当前对象的话,可以把当前对象 this传递过去
//把当前的onclick委托给 现在状态的handler
//这里我不关心当前这个状态是什么,我只是关心当前这个状态会发生什么,所以去除了if else
}
</script>
</body>
</html>