產品經理的流程總是變,所以我搬出了大殺器狀態機模式


大家好,今天給大家介紹一個新的設計模式,這個設計模式非常重要,在我們日常的開發工作當中經常使用。它就是大名鼎鼎的狀態機模式

狀態機模式非常適合用在復雜的流程或者是系統當中,可以方便我們對系統的某一個狀態進行抽象,這會讓我們編碼具有更強的可讀性以及延展性。

有向圖與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()

整個狀態機的設計模式本身並不復雜,更多的是對這個設計理念和思想的理解,代碼和形式都是表象。

好了,今天的文章就到這里,衷心祝願大家每天都有所收獲。如果還喜歡今天的內容的話,請來一個三連支持吧~(點贊、關注、轉發


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM