如何用狀態(tài)機(jī)簡(jiǎn)化代碼中復(fù)雜的 If Else 邏輯
本文轉(zhuǎn)載自微信公眾號(hào)「神光的編程秘籍」,作者神說要有光zxg。轉(zhuǎn)載本文請(qǐng)聯(lián)系神光的編程秘籍公眾號(hào)。
不知道你有沒有遇到代碼邏輯特別復(fù)雜的情況,需要很多的 if else 來判斷,每種情況有自己的邏輯。這樣的 if else 特別多以后,閱讀代碼和繼續(xù)迭代都變得很困難。如果你遇到這種代碼,會(huì)怎么優(yōu)化呢?
本文就提供一種思路,通過狀態(tài)機(jī)來簡(jiǎn)化復(fù)雜的 if else 代碼邏輯。
讀完本文,你會(huì)了解到:
- 什么是狀態(tài)機(jī)
- 什么是狀態(tài)自動(dòng)機(jī)
- typescript 源碼是怎么利用狀態(tài)機(jī)使流程更清晰的
- 詞法分析中的狀態(tài)機(jī)
- 業(yè)務(wù)代碼中如何使用狀態(tài)機(jī)
什么是狀態(tài)機(jī)
當(dāng)處理的情況特別多,我們把每種情況的處理邏輯封裝成一個(gè)狀態(tài),然后不同情況之間的轉(zhuǎn)換變成狀態(tài)的轉(zhuǎn)換。這種代碼組織形式就是狀態(tài)機(jī)。
當(dāng)每個(gè)狀態(tài)知道輸入某一段內(nèi)容時(shí)轉(zhuǎn)到哪一個(gè)狀態(tài),在一個(gè)循環(huán)內(nèi)自動(dòng)進(jìn)行狀態(tài)的流轉(zhuǎn)和不同狀態(tài)的處理,這種叫做狀態(tài)自動(dòng)機(jī)(automation),如果一個(gè)狀態(tài)在一種輸入下只有一個(gè)后續(xù)狀態(tài),這種就叫做確定性有限狀態(tài)自動(dòng)機(jī)(DFA)。
狀態(tài)之間的流轉(zhuǎn)可以通過狀態(tài)轉(zhuǎn)換圖來表示。
typescript 源碼中的狀態(tài)機(jī)
typescript compiler 就是通過狀態(tài)機(jī)來組織整個(gè)編譯流程的:
首先 tsc 劃分了很多狀態(tài),每種狀態(tài)處理一種邏輯。比如:
- CreateProgram 把源碼 parse 成 ast
- SyntaxDiagnostics 處理語(yǔ)法錯(cuò)誤
- SemanticDiagnostics 處理語(yǔ)義錯(cuò)誤
- Emit 生成目標(biāo)代碼
typescript 就通過這種狀態(tài)的修改來完成不同處理邏輯的流轉(zhuǎn),如果處理到結(jié)束狀態(tài)就代表流程結(jié)束。
這樣使得整體流程可以很輕易的擴(kuò)展和修改,比如想擴(kuò)展一個(gè)階段,只要增加一個(gè)狀態(tài),想修改某種狀態(tài)的處理邏輯,只需要修改下狀態(tài)機(jī)的該狀態(tài)的轉(zhuǎn)向。而不是大量的 if else 混雜在一起,難以擴(kuò)展和修改。
可以看到,狀態(tài)機(jī)使得 typescript 的編譯步驟可以靈活的擴(kuò)展和修改。
詞法分析中的狀態(tài)機(jī)
其實(shí)狀態(tài)機(jī)最常用的地方是用于詞法分析,因?yàn)槊總€(gè) token 都是一種處理情況,自然會(huì)有很多 if else。
像下面這樣用 if else 來做分詞自然也可以,這是 wenyan 的詞法分析邏輯,但是代碼很難維護(hù)。
更好的做法是使用狀態(tài)機(jī)(DFA)來做分詞,把每一種 token 的處理封裝成一個(gè)狀態(tài)。通過邊界條件的判斷來做狀態(tài)流轉(zhuǎn),比如某個(gè) wxml parser 分了這些狀態(tài):
每種狀態(tài)處理一種情況的 token 的識(shí)別:
通過狀態(tài)的變化驅(qū)動(dòng)處理邏輯的流轉(zhuǎn):
這樣不斷的進(jìn)行各狀態(tài)之間的流轉(zhuǎn),當(dāng)處理到字符串的末尾的時(shí)候,就完成了所有的分詞。
業(yè)務(wù)代碼中的狀態(tài)機(jī)
業(yè)務(wù)代碼中當(dāng)遇到各種 if else 的判斷的時(shí)候同樣可以用狀態(tài)機(jī)來優(yōu)化。把每種情況封裝成一個(gè)狀態(tài),通過某一種條件觸發(fā)狀態(tài)的流轉(zhuǎn),然后在狀態(tài)機(jī)里面選擇不同的狀態(tài)處理邏輯進(jìn)行處理。
不管是游戲中不同狀態(tài)做不同的處理邏輯,還是在 ui 項(xiàng)目中不同狀態(tài)做不同的渲染,當(dāng)代碼邏輯復(fù)雜時(shí),難免會(huì)有很多 if else,這時(shí)候都可以用狀態(tài)機(jī)的思路來做優(yōu)化。
這樣,當(dāng)后續(xù)擴(kuò)展處理邏輯、修改不同條件下的處理邏輯都變得簡(jiǎn)單和清晰很多。
總結(jié)
我們首先明確了狀態(tài)機(jī)的概念:通過不同狀態(tài)封裝不同情況的處理邏輯,通過狀態(tài)的修改來完成處理邏輯之間的流轉(zhuǎn)。
如果每種狀態(tài)都知道下一個(gè)狀態(tài)是什么,在一個(gè)循環(huán)內(nèi)自動(dòng)完成狀態(tài)流轉(zhuǎn)的狀態(tài)機(jī),就是狀態(tài)自動(dòng)機(jī),當(dāng)狀態(tài)為有限個(gè)時(shí),就是有限狀態(tài)自動(dòng)機(jī)(DFA)。
typescript compiler 就是通過狀態(tài)自動(dòng)機(jī)來進(jìn)行處理,封裝了很多個(gè)狀態(tài),每個(gè)狀態(tài)知道下一個(gè)狀態(tài)是什么,直到處理到終止?fàn)顟B(tài),就結(jié)束編譯。
詞法分析中一般會(huì)使用有限狀態(tài)自動(dòng)機(jī)(DFA)來處理,不同 token 用不同的狀態(tài)來處理,通過輸入字符的不同來做狀態(tài)的流轉(zhuǎn),處理完字符串就完成了分詞。
業(yè)務(wù)代碼中也經(jīng)常會(huì)有不同情況做不同的處理,這些情況在一定的條件時(shí)會(huì)做轉(zhuǎn)換的場(chǎng)景,比如類似開始、暫停、結(jié)束、重新開始這種。這種代碼就很適合用狀態(tài)機(jī)來優(yōu)化,不然會(huì)有很多的 if else。
總之,當(dāng)邏輯可以劃分為不同的情況,各種情況之間會(huì)相互轉(zhuǎn)換的時(shí)候就可以用狀態(tài)機(jī)來優(yōu)化,能夠免去大量的 if else,并且代碼的可讀性、可擴(kuò)展性、可維護(hù)性都會(huì)有一個(gè)很大的提升。
希望這篇文章能夠讓你知道狀態(tài)機(jī)是什么,什么時(shí)候可以用狀態(tài)機(jī),狀態(tài)機(jī)可以帶來哪些提升,真正在代碼中用起來。