FSM(Finite-State Machine)
package mainimport ("errors""fmt""reflect")type State interface {//get name of state.Name() string//是否允许同态转移.EnableSameTansit() bool//beginOnBegin()//endOnEnd()//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]StateOnChange 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.currenteStatesm.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.goIdleState beginstate changed, from none to IdleState.IdleState endMoveState beginstate changed, from IdleState to MoveState.FAILED! MoveState --> MoveState, forbid same state transitJumpState beginstate changed, from MoveState to JumpState.FAILED! JumpState --> JumpState, forbid same state transitIdleState beginstate 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)
}匿名接口。
其实大多时候,设计的目的在于提高效率,美丽的设计具有低耦合性。
