大家好,今天給大家介紹一個新的設計模式,這個設計模式非常重要,在我們日常的開發工作當中經常使用。它就是大名鼎鼎的狀态機模式。
狀态機模式非常适合用在複雜的流程或者是系統當中,可以友善我們對系統的某一個狀态進行抽象,這會讓我們編碼具有更強的可讀性以及延展性。
有向圖與DAG
首先和大家解釋一下狀态機當中這個狀态的概念,這裡的狀态指的是我們系統或者是流程當中的某一個狀态。我用我之前做過的一個活動系統來給大家舉一個例子。
比如我們現在要在網上舉辦一些活動,然後吸引使用者來參與。但是在使用者來參與活動的過程當中其實有很多的狀态需要判斷,比如說我們首先要判斷使用者是否已經登入了。如果登入了,還需要判斷使用者之前是否報名過,如果已經報名了,還需要判斷活動是否開始了等等。
那麼,我們就可以抽象出很多的狀态。比如是否登入、是否報名、未登入等等這些都是狀态。這些狀态之間可以通過一些條件進行轉移,比如在初始狀态當中,通過判斷使用者是否登入選擇轉移到未登入狀态或者是報名判斷的狀态上。我們把這其中的邏輯抽象出來,可以得到這麼一張有向無環圖。

幾乎所有的固定流程都可以抽象出這麼一張圖來,這種圖一般被縮寫成DAG(Directed Acyclic Graph)。如果不用狀态機的話,那麼我們需要編寫大量的代碼來進行判斷。就拿上述的這個邏輯舉例,我們需要至少4層if嵌套的邏輯判斷來實作這麼一個流程。
如果通過if判斷來實作的話,那麼面臨的一個問題就是這個流程是固定的。如果臨時需要改動,那麼必須要修改代碼,而我們知道不管大小公司,釋出代碼都是有嚴格的規範的,是不能随意釋出的。而使用狀态機主要解決的就是這個問題,可以把流程做成可配置的,如果需要臨時修改,隻需要修改狀态機的對應配置即可,可以規避掉代碼層面的修改。
狀态與狀态機
了解了DAG之後,我們再來看看狀态機的定義和解釋。
狀态機的官方定義是:
The intent of the STATE pattern is to distribute state-specific logic across classes that represent an object’s state.
狀态模式會将與狀态有關的邏輯分布寫在代表對象狀态的類中
這句話英文讀起來還是挺好了解的,中文相對更繞一些。簡而言之,machine是一種抽象的概念,代表某一個流程或者是原理,并不是我們了解的機器。是以狀态機也不是一個機器,它是由多個代表狀态的類組合而成的流程或者說模式。也就是說我們會把DAG當中的每一個節點(狀态)單獨實作成一個類,那麼整個DAG就是一系列狀态類構成的圖。
對于每個狀态類而言,它們的操作應該都是類似的,就是初始化、執行以及轉移。在一些系統當中,甚至可以沒有執行隻有轉移。既然所有狀态的操作都是類似的,那麼我們可以對所有的狀态抽象出統一的接口。這裡我們多了一個is_end方法,代表某一個狀态是否是整個流程的結束,如果是的話,我們就不需要繼續轉移了,直接退出即可。
class State:
def __init__(states):
pass
def determine(param):
pass
def operate():
pass
def is_end():
pass
同樣,我們可以實作狀态機的類。
class StateMachine:
def __init__():
self.node = StartState()
def init():
self.node = StartState()
def run(param):
while not self.node.is_end():
self.node = self.node.determine(param)
self.node.operate()
由于狀态之間轉移以及執行的邏輯都被封裝在不同的類當中了,是以對于狀态機而言,裡面的邏輯非常簡單,一般也不需要太大的修改。即使整個流程或者是某一個狀态的條件發生變動, 我們也隻需要修改對應節點的代碼即可,并不會影響整體,非常适合用在那些流程經常發生變動的場景。
最後,我們來看一個狀态機的使用案例。這個案例源于github,是一個将狀态機應用在收音機上的case,具體的細節檢視代碼即可。
class State:
def scan(self):
# 模拟收音機的儀表盤,隻能一個方向轉動
self.pos += 1
if self.pos == len(self.stations):
self.pos = 0
print('Scanning... Station is {} {}'.format(self.stations[self.pos], self.name))
class AmState(State):
# Am 音頻的類
def __init__(self, radio):
self.radio = radio
self.stations = ['1250', '1380', '1510']
self.pos = 0
self.name = 'AM'
def toggle_amfm(self):
# 轉移到Fm
print('Switching to FM')
self.radio.state = self.radio.fmstate
class FmState(State):
# Fm 音頻類
def __init__(self, radio):
self.radio = radio
self.stations = ['81.3', '89.1', '103.9']
self.pos = 0
self.name = 'FM'
def toggle_amfm(self):
# 轉移到Am
print('Switching to AM')
self.radio.state = self.radio.amstate
class Radio:
# 收音機的整體類,也就是狀态機類
def __init__(self):
self.amstate = AmState(self)
self.fmstate = FmState(self)
self.state = self.amstate
def toggle_amfm(self):
self.state.toggle_amfm()
def scan(self):
self.state.scan()
if __name__ == '__main__':
radio = Radio()
actions = [radio.scan] * 2 + [radio.toggle_amfm] + [radio.scan] * 2
actions *= 2
for action in actions:
action()
整個狀态機的設計模式本身并不複雜,更多的是對這個設計理念和思想的了解,代碼和形式都是表象。
好了,今天的文章就到這裡,衷心祝願大家每天都有所收獲。如果還喜歡今天的内容的話,請來一個三連支援吧~(點贊、關注、轉發)