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


    之前有小伙伴问我 async/await语法糖编译后其实是状态机模型,到底什么是状态机?

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

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

    我们以自动售货机为例,为简化演示,我们假设自动售货机只有1种商品, 故自动售货机有itemCountitemPrice 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
    ")
    	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 类继承、接口实现实在是太秀了。

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


    本文来自博客园,作者:{有态度的马甲},转载请注明原文链接:https://www.cnblogs.com/JulianHuang/p/15304184.html

    欢迎关注我的原创高价值公众号

    上海鲜花港 - 郁金香
  • 相关阅读:
    Jzoj5542 董先生的钦点
    Jzoj5542 董先生的钦点
    (各种)FFT模板
    (各种)FFT模板
    Jzoj3528 图书馆
    Jzoj3528 图书馆
    Jzoj5317 Func
    Jzoj5317 Func
    Jzoj3591 数据
    【UVa11021】Tribles
  • 原文地址:https://www.cnblogs.com/JulianHuang/p/15304184.html
Copyright © 2020-2023  润新知