最近在搞原子状态机,后续会发文章介绍。
export const enum FSM_EVENT {
START = 'start',
START_SUCCESS = 'startSuccess',
START_FAILED = 'startFailed',
STOP = 'stop',
STOP_SUCCESS = 'stopSuccess',
STOP_FAILED = 'stopFailed',
};
export const enum FSM_STATE {
IDLE,
STARTING,
RUNNING,
STOPING,
}
const Transitions = [
{
[FSM_EVENT.START]: FSM_STATE.STARTING,
},
{
[FSM_EVENT.START_SUCCESS]: FSM_STATE.RUNNING,
[FSM_EVENT.START_FAILED]: FSM_STATE.IDLE,
},
{
[FSM_EVENT.STOP]: FSM_STATE.STOPING,
},
{
[FSM_EVENT.STOP_SUCCESS]: FSM_STATE.IDLE,
[FSM_EVENT.STOP_FAILED]: FSM_STATE.RUNNING
}
] as const;
这个状态机是写死的四种状态
下面是正常的改变状态的过程
private transition(event: FSM_EVENT, ...args: any[]) {
const to = Transitions[this.state]?.[event];
if (typeof to == 'number') {
this.state = to;
return true;
}
return false;
}
}
到这里没什么问题,但是我盯着最上面的代码看,总觉得里面有重复的东西需要优化。
开始和结束有相同的状态变化过程,而且看着这张图总有种对称性,似乎我们的代码太过肤浅了,没有那个玄而又玄的味道。
直觉上来说四种状态对应着两个二进制位,00、 01、 10 、11,也可以作为数组的下标,0,1,2,3。
这里面一定可以用数学来计算。
现在有六种事件,但是抽出重复的单词,实际上还是四个单词的组合:start、stop、Success、Failed,所以这四个单词也可以用00、01、10、11表示。
export enum FSM_EVENT {
start,
stop,
Success,
Failed
}
export enum FSM_STATE {
IDLE = 0b11,
STARTING = 0b00,
RUNNING = 0b10,
STOPPING = 0b01
}
整理一下状态机:得到下面
const Transitions = [];
Transitions[0b00] = {
[FSMEvent[0b00] + FSMEvent[0b10]]: 0b10,
[FSMEvent[0b00] + FSMEvent[0b11]]: 0b11,
};
Transitions[0b01] = {
[FSMEvent[0b01] + FSMEvent[0b10]]: 0b11,
[FSMEvent[0b01] + FSMEvent[0b11]]: 0b10,
};
Transitions[0b10] = {
[FSMEvent[0b01]]: 0b01
};
Transitions[0b11] = {
[FSMEvent[0b00]]: 0b00
};
现在的问题是,如果通过输入:当前状态和事件,得到输出:下一个状态(或者不变)
我们已经把startSuccess事件拆成了,start+Success了,所以相当于有三个输入量,当前状态、X、Y。
反复观察上面的规律,可以发现 下一个状态值 ,就是 X^Y (异或)。
现在剩下如何判断是否跳转到下一个状态了。
00 状态下只接受 X =00,01状态下只接受X = 01 ,那这个好办就是state ^ X == 00
10 状态下只接受 X = 01 ,11 状态下只接受X = 00 ,这个也好办就是state ^ X == 11
再考虑一下其他情况,Y只能是10 和 11,但是下面两个状态变化中没有Y,所以给Y 为0,这样也能保证下一个状态是X^Y。
最后我们整理到一个判断里面即可:
private transition(x: FSM_EVENT.start | FSM_EVENT.stop, y: 0 | FSM_EVENT.Success | FSM_EVENT.Failed, ...args: any[]) {
const from = this.state.value;
const event = FSM_EVENT[x] + (y ? FSM_EVENT[y] : "")
if ((from ^ x) == (y ? 0 : 3)) {
this.state.value = x ^ y;
return true;
}
return false;
}
}
至此我们消灭了一坨看似无法消灭的代码。