之前有小伙伴問我 async/await語法糖編譯后其實是狀態機模型,到底什么是狀態機?
狀態機是一種行為設計模式,它允許對象在其內部狀態改變時改變其行為。看起來好像對象改變了它的類。
請仔細理解上面每一個字。
我們以自動售貨機為例,為簡化演示,我們假設自動售貨機只有1種商品, 故自動售貨機有itemCount
、itemPrice
2個屬性
不考慮動作的前后相關性,自動售貨機對外暴露4種行為:
- 給自動售貨機加貨
addItem
- 選擇商品
requestItem
- 付錢
insertMoney
- 出貨
dispenseItem
重點來了,當發生某種行為,自動售貨機會進入如下4種狀態之一, 並據此狀態做出特定動作, 之后進入另外一種狀態.....
- 有商品
hasItem
- 無商品
noItem
- 已經選好商品
itemRequested
- 已付錢
hasMoney
當對象可能處於多種不同的狀態之一、根據傳入的動作更改當前的狀態, 繼續接受后續動作,狀態再次發生變化.....
這樣的模式類比於機器引擎,周而復始的工作和狀態轉化,這也是狀態機的定語叫“機Machine”的原因。
有了以上思路,我們嘗試溝通UML 偽代碼
狀態機設計模式的偽代碼實現:
- 所謂的機器Machine維護了狀態切換的上下文
- 機器對外暴露的行為,驅動機器的狀態變更
- 機器到達特定的狀態 只具備特定的行為,其他行為是不被允許的
下面使用golang實現了 狀態機設計模型:
這里你也可以看下golang 是如何體現OOP中的類繼承、接口實現
goodMachine:狀態變更上下文
package main
import (
"fmt"
"reflect"
)
type goodMachine struct {
currentState state
itemCount int
itemPrice int
}
func newGoodMachine(itemCount, itemPrice int) *goodMachine {
v := &goodMachine{
itemCount: itemCount,
itemPrice: itemPrice,
}
if itemCount <= 0 {
v.setState(&noItemState{v}) // 實現state接口的是*noItemState 指針類型
} else {
v.setState(&hasItemState{v})
}
return v
}
func (v *goodMachine) setState(s state) {
fmt.Println("enter state: ", reflect.TypeOf(s))
v.currentState = s
}
func (v *goodMachine) requestItem() error {
return v.currentState.requestItem()
}
func (v *goodMachine) addItem(count int) error {
return v.currentState.addItem(count)
}
func (v *goodMachine) insertMoney(money int) error {
return v.currentState.insertMoney(money)
}
func (v *goodMachine) incrementItemCount(count int) {
v.itemCount += count
}
func (v goodMachine) dispenseItem() error {
return v.currentState.dispenseItem()
}
自動售貨機的對外的行為,被委托給特定的state對象
state: 自動售貨機對外暴露的行為
package main
// 代表某種狀態,能接受的某種動作
type state interface {
addItem(count int) error
requestItem() error
insertMoney(money int) error
dispenseItem() error
}
noItemState : 無商品
package main
import "fmt"
type noItemState struct {
*goodMachine // 存在匿名類型 goodMachine,類型是*goodMachine
}
// 給自動售貨機供貨-----> 有貨狀態
func (i *noItemState) addItem(count int) error {
i.incrementItemCount(count)
i.setState(&hasItemState{i.goodMachine})
return nil
}
func (i *noItemState) requestItem() error {
return fmt.Errorf("item out of stock")
}
func (i *noItemState) insertMoney(money int) error {
return fmt.Errorf("item out of stock")
}
func (i *noItemState) dispenseItem() error {
return fmt.Errorf("item out of stock")
}
// golang: 使用指針接受者實現了state接口的全部函數,那么隱式表明*noItemState 指針類型實現了State接口
注意:
noItemState 結構體內定義了 goodMachine, 就表明noItemState實現了goodMachine類 ;
指針接受者 noItemState實現了state接口的所有函數,那么我們就說*noItemState實現了state接口。 golang這種繼承、實現的做法真的好秀。
hasItemState: 有商品
package main
import "fmt"
type hasItemState struct {
*goodMachine
}
func (v *hasItemState) addItem(count int) error {
v.incrementItemCount(count)
return nil
}
// 有人選擇了商品---> 沒貨狀態/已經選定商品
func (v *hasItemState) requestItem() error {
if v.goodMachine.itemCount == 0 {
v.setState(&noItemState{v.goodMachine})
return fmt.Errorf("no item present")
}
fmt.Print("item requested\n")
v.setState(&itemRequestedState{v.goodMachine})
return nil
}
func (v *hasItemState) insertMoney(money int) error {
return fmt.Errorf("Please select item first")
}
func (v *hasItemState) dispenseItem() error {
return fmt.Errorf("Please select item first")
}
itemRequestedState: 有人選定商品
package main
import "fmt"
type itemRequestedState struct {
*goodMachine
}
func (i *itemRequestedState) addItem(count int) error {
return fmt.Errorf("shopping is in process")
}
func (i *itemRequestedState) requestItem() error {
return fmt.Errorf("item already requested")
}
// 付錢----> 已收錢狀態
func (i *itemRequestedState) insertMoney(money int) error {
if money < i.goodMachine.itemPrice {
fmt.Errorf("insert money is less, please insert %d", i.goodMachine)
}
fmt.Println("money entered is ok")
i.setState(&hasMoneyState{i.goodMachine})
return nil
}
func (i *itemRequestedState) dispenseItem() error {
return fmt.Errorf("please insert money first")
}
hasMoneyState:已付錢
package main
import "fmt"
type hasMoneyState struct {
*goodMachine
}
func (i *hasMoneyState) addItem(count int) error {
return fmt.Errorf("shopping is in process")
}
func (i *hasMoneyState) requestItem() error {
return fmt.Errorf("shopping is in process")
}
func (i *hasMoneyState) insertMoney(money int) error {
return fmt.Errorf("already pay money")
}
func (i *hasMoneyState) dispenseItem() error {
fmt.Println("dispensing item")
i.goodMachine.itemCount = i.goodMachine.itemCount - 1
if i.goodMachine.itemCount == 0 {
i.setState(&noItemState{i.goodMachine})
} else {
i.setState(&hasItemState{i.goodMachine})
}
return nil
}
main.go 執行
package main
import (
"fmt"
"log"
)
func main() {
goodMachine := newGoodMachine(1, 10)
err := goodMachine.requestItem()
if err != nil {
log.Fatalf(err.Error())
}
err = goodMachine.insertMoney(10)
if err != nil {
log.Fatalf(err.Error())
}
err = goodMachine.dispenseItem()
if err != nil {
log.Fatalf(err.Error())
}
fmt.Println()
err = goodMachine.requestItem()
if err != nil {
log.Fatalf(err.Error())
}
err = goodMachine.insertMoney(10)
if err != nil {
log.Fatal(err.Error())
}
err = goodMachine.dispenseItem()
if err != nil {
log.Fatalf(err.Error())
}
}
演示示例:
初始化了商品數量為1, 價格為10 的自動售貨機,連續掏10元錢買兩次, 隨時打印狀態,輸出如下:
enter state: *main.hasItemState
item requested
enter state: *main.itemRequestedState
money entered is ok
enter state: *main.hasMoneyState
dispensing item
enter state: *main.noItemState
2021/08/11 17:39:45 item out of stock
exit status 1
狀態機為什么定語是機器? Machine?
狀態機表現了: 對象的狀態受外界行為所影響,不斷的切換,到達特定的狀態又只能接受特定的行為, 真實生動的體現了機器Machine引擎的特征。
本文示例亦是學習golang OOP編程的范例,golang 類繼承、接口實現實在是太秀了。