先可以看下這篇博客:如何用狀態機簡化代碼中復雜的 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] || ''); }
以上代碼來源於:https://blog.csdn.net/inelm/article/details/4612936
