Before, some friends asked me that the async/await syntax is actually a state machine model after compiling. What is a state machine?

A state machine is a behavior design pattern that allows an object to change its behavior as its internal state changes. It looks as if the object has changed its class.

Please understand each word carefully.

Let’s take a vending machine as an example. To simplify the demonstration, we assume that there is only one item in the vending machine, so the vending machine has two attributes, itemCount and itemPrice

Regardless of the contextual correlation of the actions, vending machines are exposed to four kinds of behavior:

  • Restock the vending machineaddItem
  • Choose goodsrequestItem
  • payinsertMoney
  • shipmentdispenseItem

The important point is that when certain behavior occurs, the vending machine enters one of the following four states and performs specific actions according to the state, and then enters another state…..

  • There are goodshasItem
  • There is no goodsnoItem
  • The goods have been selecteditemRequested
  • Already payhasMoney

When an object may be in one of many different states, changes its current state based on incoming actions, continues to accept subsequent actions, and the state changes again…..

Such a mode is analogous to a Machine engine, with its repeated work and state transformation, which is why the attribute of a state Machine is called “Machine”.

With that in mind, we tried to communicate UML pseudocode

Pseudo-code implementation of the state machine design pattern:

  • So-called machines maintain the context of state switching
  • The behavior of a machine exposed to the outside world, driving a change in the state of the machine
  • A machine that reaches a certain state only has certain behaviors, and other behaviors are not allowed

Golang is used to implement the state machine design model: Here you can also see how Golang represents class inheritance and interface implementation in OOP

GoodMachine: State change context

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})} 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() }Copy the code

The vending machine’s external behavior is delegated to a specific state object

State: Exposed behavior of vending machine

Package main // represents some state, Type state interface {addItem(count int) error requestItem() error insertMoney(money int) error dispenseItem()  error }Copy the code

NoItemState: No item

Package main import "FMT" type noItemState struct {*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: Since all functions of the state interface are implemented using pointer acceptors, it is implied that the *noItemState pointer type implements the state interfaceCopy the code

Note:

The noItemState structure defines *goodMachine, indicating that noItemState implements the goodMachine class.

The pointer recipient *noItemState implements all the functions of the state interface, so we say *noItemState implements the state interface. Golang’s approach to inheritance and implementation is really neat.

HasItemState: there are goods

package main import "fmt" type hasItemState struct { *goodMachine } func (v *hasItemState) addItem(count int) error { Func (v *hasItemState) requestItem() error {if (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") }Copy the code

ItemRequestedState: Someone selected the item

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 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") }Copy the code

HasMoneyState: Paid

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
}
Copy the code

The 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()) } }Copy the code

Example: initialize the number of goods is 1, the price of 10 vending machine, pay 10 yuan to buy two times, print the status at any time, output is as follows:

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
Copy the code

A state machine is a machine. The Machine?

The state Machine shows that: the state of the object is affected by the external behavior, and it constantly switches. When it reaches a specific state, it can only accept a specific behavior, which truly and vividly reflects the characteristics of the Machine engine.

This article is also an example of learning Golang OOP programming, Golang class inheritance, interface implementation is too beautiful.

Making: github.com/zaozaoniao/…