Finite-State Machine的golang簡單實現

FSM(Finite-State Machine)
package main

import (
	"errors"
	"fmt"
	"reflect"
)

type State interface {
	//get name of state.
	Name() string

	//是否允許同態轉移.
	EnableSameTansit() bool

	//begin
	OnBegin()

	//end
	OnEnd()

	//if transit.
	CanTransitTo(name string) bool
}

func StateName(s State) string {
	if nil == s {
		return "none"
	}
	return reflect.TypeOf(s).Elem().Name()
}

type StateInfo struct {
	name string
}

func (si *StateInfo) Name() string {
	return si.name
}

func (si *StateInfo) setName(name string) {
	si.name = name
}

func (si *StateInfo) EnableSameTansit() bool {
	return false
}

func (si *StateInfo) OnBegin() {

}
func (si *StateInfo) OnEnd() {

}

func (si *StateInfo) CanTransitTo(name string) bool {
	return true
}

//-----------------------manager.
//transit State.
var ErrStateNotFound = errors.New("state not found.")
var ErrForbidSameStateTransit = errors.New("forbid same state transit")
var ErrCannotTransitToState = errors.New("cannot transit to state")

type StateManager struct {
	stateByName map[string]State

	OnChange func(from, to State)

	currenteState State
}

func (sm *StateManager) Get(name string) State {
	if s, ok := sm.stateByName[name]; ok {
		return s
	}
	return nil
}

func (sm *StateManager) Add(state State) {

	name := StateName(state)
	if nil != sm.Get(name) {
		panic("duplicate state:" + name)
	}

	state.(interface {
		setName(name string)
	}).setName(name)

	sm.stateByName[name] = state
}

func NewStateManager() *StateManager {
	return &StateManager{
		stateByName: make(map[string]State),
	}
}

func (sm *StateManager) CurrentState() State {
	return sm.currenteState
}

func (sm *StateManager) CanCurrTranistTo(name string) bool {
	if nil == sm.currenteState {
		return true
	}
	if sm.currenteState.Name() == name && !sm.currenteState.EnableSameTansit() {
		return false
	}
	return sm.currenteState.CanTransitTo(name)
}

func (sm *StateManager) Transit(name string) error {
	//get the next state.
	nextstate := sm.stateByName[name]

	if nil == nextstate {
		return ErrStateNotFound
	}

	if nil != sm.currenteState {
		if name == sm.currenteState.Name() && !sm.currenteState.EnableSameTansit() {
			return ErrForbidSameStateTransit
		}
		if !sm.currenteState.CanTransitTo(name) {
			return ErrCannotTransitToState
		}
		//current state end.
		sm.currenteState.OnEnd()
	}

	prestate := sm.currenteState
	sm.currenteState = nextstate

	//new state begin.
	sm.currenteState.OnBegin()

	//callback Change.
	if nil != sm.OnChange {
		sm.OnChange(prestate, sm.currenteState)
	}
	return nil
}

//----------------------

//-----------------------------------------------State....
// 閒置狀態
type IdleState struct {
	StateInfo // 使用StateInfo實現基礎接口
}

// 重新實現狀態開始
func (i *IdleState) OnBegin() {
	fmt.Println("IdleState begin")
}

// 重新實現狀態結束
func (i *IdleState) OnEnd() {
	fmt.Println("IdleState end")
}

// 移動狀態
type MoveState struct {
	StateInfo
}

func (m *MoveState) OnBegin() {
	fmt.Println("MoveState begin")
}

// 允許移動狀態互相轉換
func (m *MoveState) EnableSameTransit() bool {
	return true
}

// 跳躍狀態
type JumpState struct {
	StateInfo
}

func (j *JumpState) OnBegin() {
	fmt.Println("JumpState begin")
}

// 跳躍狀態不能轉移到移動狀態
func (j *JumpState) CanTransitTo(name string) bool {
	return name != "MoveState"
}

func main() {

	//create a Stata Manager.
	stateManager := NewStateManager()

	stateManager.OnChange = func(from, to State) {
		fmt.Printf("state changed, from %s to %s.\n", StateName(from), StateName(to))
	}

	stateManager.Add(new(IdleState))
	stateManager.Add(new(JumpState))
	stateManager.Add(new(MoveState))

	// 在不同狀態間轉移
	transitAndReport(stateManager, "IdleState")
	transitAndReport(stateManager, "MoveState")
	transitAndReport(stateManager, "MoveState")
	transitAndReport(stateManager, "JumpState")
	transitAndReport(stateManager, "JumpState")
	transitAndReport(stateManager, "IdleState")

}

// 封裝轉移狀態和輸出日誌
func transitAndReport(sm *StateManager, target string) {
	if err := sm.Transit(target); err != nil {
		fmt.Printf("FAILED! %s --> %s, %s\n\n", sm.CurrentState().Name(), target, err.Error())
	}
}

Output:

PS C:\Users\Administrator\Desktop\Test\go\goTest> go run .\finiteStateMachin.go
IdleState begin
state changed, from none to IdleState.
IdleState end
MoveState begin
state changed, from IdleState to MoveState.
FAILED! MoveState --> MoveState, forbid same state transit

JumpState begin
state changed, from MoveState to JumpState.
FAILED! JumpState --> JumpState, forbid same state transit

IdleState begin
state changed, from JumpState to IdleState.
PS C:\Users\Administrator\Desktop\Test\go\goTest>

關於有限狀態機的設計,
首先分解問題:狀態機包括狀態和狀態管理,故可以分爲兩部分來進行抽象:State和Manager;各個State之間是存在不同的轉換關係的,而這些關係由誰來掌握規則?State?Manager?從闊擴展性的方向來看,轉換規則是作爲一個State的屬性存在,所以自然由State自己實現,降低了State和Manager的耦合,也就增加了狀態的可擴展性。

所有State實例實現兩個接口:State和interface {
setName(name string)
}匿名接口。

其實大多時候,設計的目的在於提高效率,美麗的設計具有低耦合性。

Reference:

http://c.biancheng.net/view/5400.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章