先可以看下這篇部落格:如何用狀态機簡化代碼中複雜的 if else 邏輯 —— https://mp.weixin.qq.com/s/dDOA5JQQz3r4a7-yPl33Bg
一、狀态機的基本概念
當處理的情況特别多,我們把每種情況的處理邏輯封裝成一個狀态,然後不同情況之間的轉換變成狀态的轉換。這種代碼組織形式就是狀态機。
當每個狀态知道輸入某一段内容時轉到哪一個狀态,在一個循環内自動進行狀态的流轉和不同狀态的處理,這種叫做狀态自動機(automation),如果一個狀态在一種輸入下隻有一個後續狀态,這種就叫做确定性有限狀态自動機(DFA)。
二、typescript 源碼是怎麼利用狀态機使流程更清晰的
首先 tsc 劃分了很多狀态,每種狀态處理一種邏輯。比如:
- CreateProgram 把源碼 parse 成 ast
- SyntaxDiagnostics 處理文法錯誤
- SemanticDiagnostics 處理語義錯誤
- Emit 生成目标代碼

typescript 就通過這種狀态的修改來完成不同處理邏輯的流轉,如果處理到結束狀态就代表流程結束。
這樣使得整體流程可以很輕易的擴充和修改,比如想擴充一個階段,隻要增加一個狀态,想修改某種狀态的處理邏輯,隻需要修改下狀态機的該狀态的轉向。而不是大量的 if else 混雜在一起,難以擴充和修改。可以看到,狀态機使得 ts 的編譯步驟可以靈活的擴充和修改。
三、詞法分析中的狀态機
其實狀态機最常用的地方是用于詞法分析,因為每個 token 都是一種處理情況,自然會有很多 if else。
更好的做法是使用狀态機(DFA)來做分詞,把每一種 token 的處理封裝成一個狀态。通過邊界條件的判斷來做狀态流轉,比如某個 wxml parser 分了這些狀态:
每種狀态處理一種情況的 token 的識别:
四、業務代碼中的狀态機
業務代碼中當遇到各種 if else 的判斷的時候同樣可以用狀态機來優化。把每種情況封裝成一個狀态,通過某一種條件觸發狀态的流轉,然後在狀态機裡面選擇不同的狀态處理邏輯進行處理。(其實下面總體來說還是用的 switch case結構)
總結一下:
1、我們首先明确了狀态機的概念:通過不同狀态封裝不同情況的處理邏輯,通過狀态的修改來完成處理邏輯之間的流轉。
2、如果每種狀态都知道下一個狀态是什麼,在一個循環内自動完成狀态流轉的狀态機,就是狀态自動機,當狀态為有限個時,就是有限狀态自動機(DFA)。
3、typescript compiler 就是通過狀态自動機來進行處理,封裝了很多個狀态,每個狀态知道下一個狀态是什麼,直到處理到終止狀态,就結束編譯。
4、詞法分析中一般會使用有限狀态自動機(DFA)來處理,不同 token 用不同的狀态來處理,通過輸入字元的不同來做狀态的流轉,處理完字元串就完成了分詞。
5、業務代碼中也經常會有不同情況做不同的處理,這些情況在一定的條件時會做轉換的場景,比如類似開始、暫停、結束、重新開始這種。這種代碼就很适合用狀态機來優化,不然會有很多的 if else。
總之,當邏輯可以劃分為不同的情況,各種情況之間會互相轉換的時候就可以用狀态機來優化,能夠免去大量的 if else,并且代碼的可讀性、可擴充性、可維護性都會有一個很大的提升。
五、用狀态機模式思想消除代碼裡複雜的 if else 邏輯
前一陣開發的一個 web 界面上有很多諸如“按鈕隐藏顯示”,“邊框隐藏顯示”,“伸縮” 等效果的切換,在展示不同内容的時候,這些配套的顯示控件需要跟着切換不同的狀态。迫于進度,使用的是 if..else, 或者 switch..case 的繁雜的 js 代碼來實作這些狀态的判斷和轉換。js 代碼很快到了 400~500行,變得很難了解。并且我要加入新的狀态切換的時候感覺比較困難。今天決心重構,于是忽然聯想起狀态機 (State Machine) 模式,不正好在這裡能用上嗎?而 js 中的對象表示文法正好非常友善構造“狀态表”和“狀态輪換表”,花了1個多小時,完成了這個工作。重構後代碼的邏輯可謂豁然開朗,帶來的僅僅是配置上的稍許備援,但是這個完全可以接受的。
// 狀态表定義
var statusTable = {
'狀态1': {
sizeCode: 1 ,
headerUrl: ' / test1 / test2',
bodyUrl: 'about:blank',
showTitle: true ,
showBorder: true ,
showMin: true ,
showMax: false ,
showClose: true
},
'狀态3': {
sizeCode: 2 ,
headerUrl: ' / test1 / test2',
bodyUrl: ' / test1 / test2',
showTitle: true ,
showBorder: true ,
showMin: true ,
showMax: true ,
showClose: true
},
'狀态4': {
sizeCode: 3 ,
headerUrl: ' / test1 / test2',
bodyUrl: ' / test1 / test2',
showTitle: true ,
showBorder: true ,
showMin: true ,
showMax: false ,
showClose: true
},
'狀态2': {
sizeCode: 2 ,
headerUrl: ' / test1 / test2',
bodyUrl: ' / test1 / test2',
showTitle: true ,
showBorder: true ,
showMin: true ,
showMax: false ,
showClose: true
}
};
// 目前狀态碼
var currentStatusCode = '';
// 切換到狀态機某個狀态
function loadStatus(code){
var status = statusTable[code] || null ;
if ( ! status) return ;
// update other status
// 利用 status 做一系列設定。。如顯示隐藏按鈕等
currentStatusCode = code;
}
// 示例函數 foo 和 bar.
// 一個函數是一套自定義的邏輯,定義一個狀态輪換表即可實作。
function foo(){
var jumpTable = {
'狀态1': '',
'狀态2': '狀态1',
'狀态3': '狀态4',
'狀态4': ''
};
loadStatus(jumpTable[currentStatusCode] || '');
}
function bar(){
var jumpTable = {
'狀态1': '狀态2',
'狀态2': '',
'狀态3': '狀态1',
'狀态4': '狀态3'
};
loadStatus(jumpTable[currentStatusCode] || '');
}
以上代碼來源于: