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

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