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