[翻譯] Go(#golang) 實現的狀態機器

來源:互聯網
上載者:User
這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。

春節前,粗略研究了一下的公用帳號。用 Golang 實現了一個簡單的 package wechat,用於接入公用帳號。當時就在思考,的文字互動過程如果要實現有一定邏輯的複雜過程,可能需要使用到狀態機器。然後,就看到了這篇文章:《State machines in Go (#golang)》。非常合時宜啊!翻譯於此,以饗讀者!

——–翻譯分隔線——–

Go(#golang) 實現的狀態機器

我已經用 Go 代替 Python 重寫了一個關鍵的服務元件。由於 Python 的解譯器不是安全執行緒的,所以在解析的時候使用了全域鎖。Go 與 Python 不同,它內建了並發支援,並且是靜態編譯的。

首先要實現一個狀態機器。Python 的版本是基於 David Mertz 的這篇文章。

Mertz 使用了物件導向的形式,定義了一個有著資料和方法的類。他的代碼,拋開文法不談,對於任何有著 C++、C# 和 Java 的物件導向經驗的人來說都不會陌生。

不過 Go 沒有提供在特定資料結構上內部關聯方法的機制。作為代替,Go 允許聯合方法到資料結構,這樣任何方法都可以應用到任何結構上。(譯註:class { methods } 和 struct { }; methods 的區別。)

這種形式與 Alan Kay 所表達的,關於最初的物件導向階段比較接近。

先別忘了這些,下面是我最開始用 Go 結構體編寫的狀態機器的類:

type Machine struct {    Handlers   map[string]func(interface{}) (string, interface{})    StartState string    EndStates  map[string]bool}

跟 Mertz 的定義一樣,Handlers 是一個用 string 做鍵名的 map,map 項儲存的值是函數,可以接收一個“物料”,並且返回下一個狀態名的字串和更新後的物料值。

Go 認為函數是一等公民對象,因此將它們在狀態之間儲存和傳遞跟在 Python 中的方式一樣。

我僅僅在狀態列表的最後做了一些改變:Mertz 使用一個字串的列表,但由於沒有辦法在 Go 的列表中進行快速的定位,我使用了 map(在 Go 中,只能通過迭代遍曆整個字串列表,直到找到一個匹配項)。

由於處理函數的原型比較笨重,我為其建立了一個自訂函數類型:

type Handler func(interface{}) (string, interface{})type Machine struct {    Handlers   map[string]Handler    StartState string    EndStates  map[string]bool}

剩下的就是定義 Machine 結構體關聯的方法。

首先定義的兩個方法,一個提供了狀態名關聯到處理函數,另一個設定了結束狀態:

func (machine *Machine) AddState(handlerName string, handlerFn Handler) {    machine.Handlers[handlerName] = handlerFn}func (machine *Machine) AddEndState(endState string) {    machine.EndStates[endState] = true}

值得說明的是由於 EndStates 是一個 map(在 Mertz 原始的版本中是 list),所以可以有多個終止處理過程的狀態。

最後一個方法用於執行狀態機器,應用恰當的處理函數,並在到達結束狀態時終止。

由於函數集合作為一等公民對象儲存在 map 中,基於名字找到它們並且進行調用是很輕鬆的:

func (machine *Machine) Execute(cargo interface{}) {    if handler, present := machine.Handlers[machine.StartState]; present {        for {            nextState, nextCargo := handler(cargo)            _, finished := machine.EndStates[nextState]            if finished {                break            } else {                handler, present = machine.Handlers[nextState]                cargo = nextCargo            }        }    }}

唯一美中不足的是 Go 的強型別,在處理函數的原型中,需要指定物料的類型。

使用通用的 interface{} 作為類型,所有處理函數都需要對輸入的物料進行類型斷言,這樣它們就可以處理任何資料(測試的例子使用了浮點作為物料,不過其實它可以是任何資料類型,甚至是自訂的結構體)。

完整的狀態機器已經作為 Go 包發布。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.