参考文档:
https://www.cnblogs.com/double12gzh/p/13621445.html
有限状态机(FSM)是一个抽象的机器,任何时刻该机器都处于某个状态,且状态总数有限。该机器可以根据输入,从一个状态转换到另一个状态中。
三个关键要素:初始状态,可能状态列表,触发状态变化的输入
第一种实现思路
package main
import(
"bufio"
"fmt"
"log"
"os"
"strings"
)
type State uint32
const (
Locked State = iota
Unlocked
)
const (
dzToubi = "coin"
dzTuiGan = "push"
)
func main() {
state := Locked //初始状态,锁定
prompt(state)
reader := bufio.NewReader(os.Stdin)
for {
cmd, err := reader.ReadString('\n')
if err != nil {
log.Fatalln(err)
}
cmd = strings.TrimSpace(cmd)
switch state {
case Locked:
if cmd == dzToubi {
fmt.Println("解锁,请通行")
state = Unlocked
} else if cmd == dzTuiGan {
fmt.Println("禁止通行,请先解锁")
} else {
fmt.Println("无效命令,请重新输入")
}
case Unlocked:
if cmd == dzToubi {
fmt.Println("门已开,投币算是慈善了")
} else if cmd == dzTuiGan {
fmt.Println("请通行,随后道闸关闭")
state = Locked
} else {
fmt.Println("无效命令,请重新输入")
}
}
}
}
func prompt(s State) {
m := map[State]string {
Locked: "关闭",
Unlocked: "开通",
}
fmt.Printf("当前闸机状态为:[%s],你下一步的动作是:[ coin | push ]\n", m[s])
}
优化后的解法
package main
import(
"bufio"
"fmt"
"log"
"os"
"strings"
)
type State uint32
const (
Locked State = iota
Unlocked
)
const (
CmdCoin = "coin"
CmdPush = "push"
)
type Turnstile struct {
s State
}
func (p *Turnstile) ExecuteCmd( cmd string ) {
//以当前状态和收到的命令构成一个二元组
tupple := StateCmdTupple{p.s, strings.TrimSpace(cmd)}
//通过二元组查询状态转换表,获得一个转换函数
//本例中,这个表是通过map实现的,二元组是Key
if f := StateTransitionTable[tupple]; f == nil {
fmt.Println("无效命令,请重试")
} else {
f(&p.s)
}
}
type StateCmdTupple struct {
s State
c string
}
type TransitionFunc func(s *State)
var StateTransitionTable = map[StateCmdTupple]TransitionFunc {
{Locked, CmdCoin}: func(s *State){
fmt.Println("已解锁,请通行。")
*s = Unlocked
},
{Locked, CmdPush}: func(s *State){
fmt.Println("禁止通行,请先投币。")
},
{Unlocked, CmdCoin}: func(s *State){
fmt.Println("多投的币会捐给国家。")
},
{Unlocked, CmdPush}: func(s *State){
fmt.Println("请尽快通行,通过后闸机锁定。")
*s = Locked
},
}
func prompt(s State) {
m := map[State]string{
Locked: "锁定",
Unlocked: "解锁",
}
fmt.Printf("闸机当前的状态是: [%s], 请输入命令(coin或push)\n", m[s])
}
func main() {
machine := &Turnstile{Locked}
prompt(machine.s)
r := bufio.NewReader(os.Stdin)
for {
cmd, err := r.ReadString('\n')
if err != nil {
log.Fatalln(err)
}
machine.ExecuteCmd(cmd)
}
}