我是状态机,一颗永远骚动的机器引擎

本文转载自微信公众号「精益码农」,作者小码甲 。转载本文请联系精益码农公众号。

成都创新互联-专业网站定制、快速模板网站建设、高性价比临澧网站开发、企业建站全套包干低至880元,成熟完善的模板库,直接使用。一站式临澧网站制作公司更省心,省钱,快速模板网站建设找我们,业务覆盖临澧地区。费用合理售后完善,10年实体公司更值得信赖。

状态机是一种行为设计模式,它允许对象在其内部状态改变时改变其行为。看起来好像对象改变了它的类。

请仔细理解上面每一个字。

我们以自动售货机为例,为简化演示,我们假设自动售货机只有1种商品, 故自动售货机有itemCount 、itemPrice 2个属性。

不考虑动作的前后相关性,自动售货机对外暴露4种行为:

  • 给自动售货机加货 addItem
  • 选择商品 requestItem
  • 付钱 insertMoney
  • 出货 dispenseItem

重点来了,当发生某种行为,自动售货机会进入如下4种状态之一, 并据此状态做出特定动作, 之后进入另外一种状态.....

  • 有商品 hasItem
  • 无商品 noItem
  • 已经选好商品 itemRequested
  • 已付钱 hasMoney

当对象可能处于多种不同的状态之一、根据传入的动作更改当前的状态, 继续接受后续动作,状态再次发生变化.....

这样的模式类比于机器引擎,周而复始的工作和状态转化,这也是状态机的定语叫“机Machine”的原因。

有了以上思路,我们尝试沟通UML 伪代码:

状态机设计模式的伪代码实现:

  • 所谓的机器Machine维护了状态切换的上下文
  • 机器对外暴露的行为,驱动机器的状态变更, 行为和状态是有因果关系的
  • 机器到达特定的状态 只具备特定的行为,其他行为是不被允许的, 这在外面看,貌似是对象改变了原类的行为

下面使用golang实现了 状态机设计模型:这里你也可以看下golang 是如何体现OOP中的类继承、接口实现

goodMachine:状态变更上下文

 
 
 
 
  1. package main 
  2.  
  3. import ( 
  4.  "fmt" 
  5.  "reflect" 
  6.  
  7. type goodMachine struct { 
  8.  currentState state 
  9.  itemCount    int 
  10.  itemPrice    int 
  11.  
  12. func newGoodMachine(itemCount, itemPrice int) *goodMachine { 
  13.  v := &goodMachine{ 
  14.   itemCount: itemCount, 
  15.   itemPrice: itemPrice, 
  16.  } 
  17.  if itemCount <= 0 { 
  18.   v.setState(&noItemState{v}) // 实现state接口的是*noItemState 指针类型 
  19.  } else { 
  20.   v.setState(&hasItemState{v}) 
  21.  } 
  22.  return v 
  23.  
  24. func (v *goodMachine) setState(s state) { 
  25.  fmt.Println("enter state: ", reflect.TypeOf(s)) 
  26.  v.currentState = s 
  27.  
  28. func (v *goodMachine) requestItem() error { 
  29.  return v.currentState.requestItem() 
  30.  
  31. func (v *goodMachine) addItem(count int) error { 
  32.  return v.currentState.addItem(count) 
  33.  
  34. func (v *goodMachine) insertMoney(money int) error { 
  35.  return v.currentState.insertMoney(money) 
  36.  
  37. func (v *goodMachine) incrementItemCount(count int) { 
  38.  v.itemCount += count 
  39.  
  40. func (v goodMachine) dispenseItem() error { 
  41.  return v.currentState.dispenseItem() 

自动售货机对外的行为,被委托给特定的state对象

state:自动售货机对外暴露的行为

 
 
 
 
  1. package main 
  2.  
  3. //  代表某种状态,能接受的某种动作 
  4. type state interface { 
  5.  addItem(count int) error 
  6.  requestItem() error 
  7.  insertMoney(money int) error 
  8.  dispenseItem() error 

noItemState : 无商品

 
 
 
 
  1. package main 
  2.  
  3. import "fmt" 
  4.  
  5. type noItemState struct { 
  6.  *goodMachine // 存在匿名类型 goodMachine,类型是*goodMachine 
  7.  
  8. //  给自动售货机供货-----> 有货状态 
  9. func (i *noItemState) addItem(count int) error { 
  10.  i.incrementItemCount(count) 
  11.  i.setState(&hasItemState{i.goodMachine}) 
  12.  return nil 
  13.  
  14. func (i *noItemState) requestItem() error { 
  15.  return fmt.Errorf("item out of  stock") 
  16.  
  17. func (i *noItemState) insertMoney(money int) error { 
  18.  return fmt.Errorf("item out of stock") 
  19.  
  20. func (i *noItemState) dispenseItem() error { 
  21.  return fmt.Errorf("item out of stock") 
  22.  
  23. // golang: 使用指针接受者实现了state接口的全部函数,那么隐式表明*noItemState 指针类型实现了State接口 

注意:noItemState 结构体内定义了 goodMachine, 就表明noItemState继承了goodMachine类 ;

指针接受者 noItemState实现了state接口的所有函数,那么我们就说*noItemState实现了state接口。

hasItemState: 有商品

 
 
 
 
  1. package main 
  2.  
  3. import "fmt" 
  4.  
  5. type hasItemState struct { 
  6.  *goodMachine 
  7.  
  8. func (v *hasItemState) addItem(count int) error { 
  9.  v.incrementItemCount(count) 
  10.  return nil 
  11.  
  12. // 有人选择了商品---> 没货状态/已经选定商品 
  13. func (v *hasItemState) requestItem() error { 
  14.  if v.goodMachine.itemCount == 0 { 
  15.   v.setState(&noItemState{v.goodMachine}) 
  16.   return fmt.Errorf("no item present") 
  17.  } 
  18.  
  19.  fmt.Print("item  requested\n") 
  20.  v.setState(&itemRequestedState{v.goodMachine}) 
  21.  return nil 
  22.  
  23. func (v *hasItemState) insertMoney(money int) error { 
  24.  return fmt.Errorf("Please select item first") 
  25.  
  26. func (v *hasItemState) dispenseItem() error { 
  27.  return fmt.Errorf("Please select item first") 

itemRequestedState:有人选定商品

 
 
 
 
  1. package main 
  2.  
  3. import "fmt" 
  4.  
  5. type itemRequestedState struct { 
  6.  *goodMachine 
  7.  
  8. func (i *itemRequestedState) addItem(count int) error { 
  9.  return fmt.Errorf("shopping is  in  process") 
  10.  
  11. func (i *itemRequestedState) requestItem() error { 
  12.  return fmt.Errorf("item already requested") 
  13.  
  14. // 付钱----> 已收钱状态 
  15. func (i *itemRequestedState) insertMoney(money int) error { 
  16.  if money < i.goodMachine.itemPrice { 
  17.   fmt.Errorf("insert money is less, please insert %d", i.goodMachine) 
  18.  } 
  19.  fmt.Println("money entered is ok") 
  20.  i.setState(&hasMoneyState{i.goodMachine}) 
  21.  return nil 
  22. func (i *itemRequestedState) dispenseItem() error { 
  23.  return fmt.Errorf("please insert money first") 

hasMoneyState:已付钱

 
 
 
 
  1. package main 
  2.  
  3. import "fmt" 
  4.  
  5. type hasMoneyState struct { 
  6.  *goodMachine 
  7.  
  8. func (i *hasMoneyState) addItem(count int) error { 
  9.  return fmt.Errorf("shopping is in process") 
  10. func (i *hasMoneyState) requestItem() error { 
  11.  return fmt.Errorf("shopping is in process") 
  12. func (i *hasMoneyState) insertMoney(money int) error { 
  13.  return fmt.Errorf("already pay money") 
  14. func (i *hasMoneyState) dispenseItem() error { 
  15.  fmt.Println("dispensing item") 
  16.  i.goodMachine.itemCount = i.goodMachine.itemCount - 1 
  17.  if i.goodMachine.itemCount == 0 { 
  18.   i.setState(&noItemState{i.goodMachine}) 
  19.  } else { 
  20.   i.setState(&hasItemState{i.goodMachine}) 
  21.  } 
  22.  return nil 

main.go 执行

 
 
 
 
  1. package main 
  2.  
  3. import ( 
  4.   "fmt" 
  5.   "log" 
  6.  
  7. func main() { 
  8.   goodMachine := newGoodMachine(1, 10) 
  9.   err := goodMachine.requestItem() 
  10.   if err != nil { 
  11.     log.Fatalf(err.Error()) 
  12.   } 
  13.  
  14.   err = goodMachine.insertMoney(10) 
  15.   if err != nil { 
  16.     log.Fatalf(err.Error()) 
  17.   } 
  18.  
  19.   err = goodMachine.dispenseItem() 
  20.   if err != nil { 
  21.     log.Fatalf(err.Error()) 
  22.   } 
  23.   fmt.Println() 
  24.  
  25.   err = goodMachine.requestItem() 
  26.   if err != nil { 
  27.     log.Fatalf(err.Error()) 
  28.   } 
  29.   err = goodMachine.insertMoney(10) 
  30.   if err != nil { 
  31.     log.Fatal(err.Error()) 
  32.   } 
  33.   err = goodMachine.dispenseItem() 
  34.   if err != nil { 
  35.     log.Fatalf(err.Error()) 
  36.   } 

初始化了商品数量为1,价格为10 的自动售货机,连续掏10元钱买两次, 随时打印状态, 输出如下:

 
 
 
 
  1. enter state:  *main.hasItemState 
  2. item  requested 
  3. enter state:  *main.itemRequestedState 
  4. money entered is ok 
  5. enter state:  *main.hasMoneyState      
  6. dispensing item 
  7. enter state:  *main.noItemState        
  8.  
  9. 2021/08/11 17:39:45 item out of  stock 
  10. exit status 1 

状态机为什么定语是机器?Machine?

状态机表现了:

对象的状态受外界行为所影响,不断的切换,到达特定的状态又只能接受特定的行为, 真实生动的体现了机器Machine引擎的特征。

本文示例亦是学习golang OOP编程的范例,golang 类继承、接口实现实在是太秀了。

github: https://github.com/zaozaoniao/statemachine

网页名称:我是状态机,一颗永远骚动的机器引擎
文章分享:http://www.gawzjz.com/qtweb/news27/198327.html

网站建设、网络推广公司-创新互联,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等

广告

声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 创新互联