我是状态机,有一颗永远骚动的机器引擎
之前有小伙伴问我 async/await语法糖编译后其实是状态机模型,到底什么是状态机?
状态机是一种行为设计模式,它允许对象在其内部状态改变时改变其行为。看起来好像对象改变了它的类。
请仔细理解上面每一个字。
我们以自动售货机为例,为简化演示,我们假设自动售货机只有1种商品, 故自动售货机有itemCount
、itemPrice
2个属性
不考虑动作的前后相关性,自动售货机对外暴露4种行为:
- 给自动售货机加货
addItem
- 选择商品
requestItem
- 付钱
insertMoney
- 出货
dispenseItem
重点来了,当发生某种行为,自动售货机会进入如下4种状态之一, 并据此状态做出特定动作, 之后进入另外一种状态.....
- 有商品
hasItem
- 无商品
noItem
- 已经选好商品
itemRequested
- 已付钱
hasMoney
当对象可能处于多种不同的状态之一、根据传入的动作更改当前的状态, 继续接受后续动作,状态再次发生变化.....
这样的模式类比于机器引擎,周而复始的工作和状态转化,这也是状态机的定语叫“机Machine”的原因。
有了以上思路,我们尝试沟通UML 伪代码
状态机设计模式的伪代码实现:
- 所谓的机器Machine维护了状态切换的上下文
- 机器对外暴露的行为,驱动机器的状态变更
- 机器到达特定的状态 只具备特定的行为,其他行为是不被允许的
下面使用golang实现了 状态机设计模型:
这里你也可以看下golang 是如何体现OOP中的类继承、接口实现
goodMachine:状态变更上下文
package main import ( "fmt" "reflect" ) type goodMachine struct { currentState state itemCount int itemPrice int } func newGoodMachine(itemCount, itemPrice int) *goodMachine { v := &goodMachine{ itemCount: itemCount, itemPrice: itemPrice, } if itemCount <= 0 { v.setState(&noItemState{v}) // 实现state接口的是*noItemState 指针类型 } else { v.setState(&hasItemState{v}) } return v } func (v *goodMachine) setState(s state) { fmt.Println("enter state: ", reflect.TypeOf(s)) v.currentState = s } func (v *goodMachine) requestItem() error { return v.currentState.requestItem() } func (v *goodMachine) addItem(count int) error { return v.currentState.addItem(count) } func (v *goodMachine) insertMoney(money int) error { return v.currentState.insertMoney(money) } func (v *goodMachine) incrementItemCount(count int) { v.itemCount += count } func (v goodMachine) dispenseItem() error { return v.currentState.dispenseItem() }
自动售货机的对外的行为,被委托给特定的state对象
state: 自动售货机对外暴露的行为
package main // 代表某种状态,能接受的某种动作 type state interface { addItem(count int) error requestItem() error insertMoney(money int) error dispenseItem() error }
noItemState : 无商品
package main import "fmt" type noItemState struct { *goodMachine // 存在匿名类型 goodMachine,类型是*goodMachine } // 给自动售货机供货-----> 有货状态 func (i *noItemState) addItem(count int) error { i.incrementItemCount(count) i.setState(&hasItemState{i.goodMachine}) return nil } func (i *noItemState) requestItem() error { return fmt.Errorf("item out of stock") } func (i *noItemState) insertMoney(money int) error { return fmt.Errorf("item out of stock") } func (i *noItemState) dispenseItem() error { return fmt.Errorf("item out of stock") } // golang: 使用指针接受者实现了state接口的全部函数,那么隐式表明*noItemState 指针类型实现了State接口
注意:
noItemState 结构体内定义了 goodMachine, 就表明noItemState实现了goodMachine类 ;
指针接受者 noItemState实现了state接口的所有函数,那么我们就说*noItemState实现了state接口。 golang这种继承、实现的做法真的好秀。
hasItemState: 有商品
package main import "fmt" type hasItemState struct { *goodMachine } func (v *hasItemState) addItem(count int) error { v.incrementItemCount(count) return nil } // 有人选择了商品---> 没货状态/已经选定商品 func (v *hasItemState) requestItem() error { if v.goodMachine.itemCount == 0 { v.setState(&noItemState{v.goodMachine}) return fmt.Errorf("no item present") } fmt.Print("item requested\n") v.setState(&itemRequestedState{v.goodMachine}) return nil } func (v *hasItemState) insertMoney(money int) error { return fmt.Errorf("Please select item first") } func (v *hasItemState) dispenseItem() error { return fmt.Errorf("Please select item first") }
itemRequestedState: 有人选定商品
package main import "fmt" type itemRequestedState struct { *goodMachine } func (i *itemRequestedState) addItem(count int) error { return fmt.Errorf("shopping is in process") } func (i *itemRequestedState) requestItem() error { return fmt.Errorf("item already requested") } // 付钱----> 已收钱状态 func (i *itemRequestedState) insertMoney(money int) error { if money < i.goodMachine.itemPrice { fmt.Errorf("insert money is less, please insert %d", i.goodMachine) } fmt.Println("money entered is ok") i.setState(&hasMoneyState{i.goodMachine}) return nil } func (i *itemRequestedState) dispenseItem() error { return fmt.Errorf("please insert money first") }
hasMoneyState:已付钱
package main import "fmt" type hasMoneyState struct { *goodMachine } func (i *hasMoneyState) addItem(count int) error { return fmt.Errorf("shopping is in process") } func (i *hasMoneyState) requestItem() error { return fmt.Errorf("shopping is in process") } func (i *hasMoneyState) insertMoney(money int) error { return fmt.Errorf("already pay money") } func (i *hasMoneyState) dispenseItem() error { fmt.Println("dispensing item") i.goodMachine.itemCount = i.goodMachine.itemCount - 1 if i.goodMachine.itemCount == 0 { i.setState(&noItemState{i.goodMachine}) } else { i.setState(&hasItemState{i.goodMachine}) } return nil }
main.go 执行
package main import ( "fmt" "log" ) func main() { goodMachine := newGoodMachine(1, 10) err := goodMachine.requestItem() if err != nil { log.Fatalf(err.Error()) } err = goodMachine.insertMoney(10) if err != nil { log.Fatalf(err.Error()) } err = goodMachine.dispenseItem() if err != nil { log.Fatalf(err.Error()) } fmt.Println() err = goodMachine.requestItem() if err != nil { log.Fatalf(err.Error()) } err = goodMachine.insertMoney(10) if err != nil { log.Fatal(err.Error()) } err = goodMachine.dispenseItem() if err != nil { log.Fatalf(err.Error()) } }
演示示例:
初始化了商品数量为1, 价格为10 的自动售货机,连续掏10元钱买两次, 随时打印状态,输出如下:
enter state: *main.hasItemState item requested enter state: *main.itemRequestedState money entered is ok enter state: *main.hasMoneyState dispensing item enter state: *main.noItemState 2021/08/11 17:39:45 item out of stock exit status 1
状态机为什么定语是机器? Machine?
状态机表现了: 对象的状态受外界行为所影响,不断的切换,到达特定的状态又只能接受特定的行为, 真实生动的体现了机器Machine引擎的特征。
本文示例亦是学习golang OOP编程的范例,golang 类继承、接口实现实在是太秀了。
本文来自博客园,作者:{有态度的马甲},转载请注明原文链接:https://www.cnblogs.com/JulianHuang/p/15304184.html
欢迎关注我的原创技术、职场公众号, 加好友谈天说地,一起进化
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 一个奇形怪状的面试题:Bean中的CHM要不要加volatile?
· [.NET]调用本地 Deepseek 模型
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· .NET Core 托管堆内存泄露/CPU异常的常见思路
· PostgreSQL 和 SQL Server 在统计信息维护中的关键差异
· DeepSeek “源神”启动!「GitHub 热点速览」
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 我与微信审核的“相爱相杀”看个人小程序副业
· C# 集成 DeepSeek 模型实现 AI 私有化(本地部署与 API 调用教程)
· spring官宣接入deepseek,真的太香了~