What is the state mode
State pattern is a behavior design pattern that allows you to change the behavior of an object when its internal state changes, making it look like it has changed its class.
Why use state mode
If the object needs to perform different behaviors according to its current state, and the number of States is very large, and the code related to the state will change frequently, the state mode can be used. State patterns can be used when there are many duplicate codes in similar state and condition based state machine transitions.
How to implement state mode
Here, take the vending machine as an example. The vending machine has four states (goods, goods have been requested, notes have been received, and no goods). The behavior (selecting goods, adding goods, inserting notes, shipping) will change according to the change of state. For example, if the status is no goods, you cannot select goods; When there are commodities, you can choose commodities.
state.go
package state
type state interface {
AddItem (int) error // add item
Requestitem() error // select an item
Insertmoney (money int) error // pay
Dispenseitem() error // provide goods
}
has_item_state.go
package state
import "fmt"
//Commodity status
type hasItemState struct {
vendingMachine *vendingMachine
}
func (i *hasItemState) requestItem() error {
if i.vendingMachine.itemCount == 0 {
i.vendingMachine.setState(i.vendingMachine.noItem)
return fmt. Errorf ("no stock")
}
fmt. Printf ("select item \ n")
i.vendingMachine.setState(i.vendingMachine.itemRequested)
return nil
}
func (i *hasItemState) addItem(count int) error {
fmt. Printf ("% D, increase commodity quantity: \ n", count)
i.vendingMachine.incrementItemCount(count)
return nil
}
func (i *hasItemState) insertMoney(money int) error {
return fmt. Errorf ("please select the product first")
}
func (i *hasItemState) dispenseItem() error {
return fmt. Errorf ("please select the product first")
}
has_money_state.go
package state
import "fmt"
//Receipt of banknotes
type hasMoneyState struct {
vendingMachine *vendingMachine
}
func (i *hasMoneyState) requestItem() error {
return fmt. Errorf ("shipment in progress, please wait")
}
func (i *hasMoneyState) addItem(count int) error {
return fmt. Errorf ("shipment in progress, please wait")
}
func (i *hasMoneyState) insertMoney(money int) error {
return fmt. Errorf ("shipment in progress, please wait")
}
func (i *hasMoneyState) dispenseItem() error {
fmt. Println ("shipping in progress")
i.vendingMachine.itemCount = i.vendingMachine.itemCount - 1
if i.vendingMachine.itemCount == 0 {
i.vendingMachine.setState(i.vendingMachine.noItem)
} else {
i.vendingMachine.setState(i.vendingMachine.hasItem)
}
return nil
}
item_requested_state.go
package state
import (
"errors"
"fmt"
)
//Item selected status
type itemRequestedState struct {
vendingMachine *vendingMachine
}
func (i *itemRequestedState) requestItem() error {
return fmt. Errorf ("item selected")
}
func (i *itemRequestedState) addItem(count int) error {
return fmt. Errorf ("in sale, cannot add goods")
}
func (i *itemRequestedState) insertMoney(money int) error {
if money < i.vendingMachine.itemPrice {
fmt. Errorf ("insert amount is insufficient, please insert:% d", i.vendingmachine.itemprice)
return errors. New ("insert insufficient amount")
}
fmt. Println ("shipping in progress")
i.vendingMachine.setState(i.vendingMachine.hasMoney)
return nil
}
func (i *itemRequestedState) dispenseItem() error {
return fmt. Errorf ("please put in the coin first")
}
no_item_state.go
package state
import "fmt"
//No commodity status
type noItemState struct {
vendingMachine *vendingMachine
}
func (i *noItemState) requestItem() error {
return fmt. Errorf ("out of stock")
}
func (i *noItemState) addItem(count int) error {
i.vendingMachine.incrementItemCount(count)
i.vendingMachine.setState(i.vendingMachine.hasItem)
return nil
}
func (i *noItemState) insertMoney(money int) error {
return fmt. Errorf ("out of stock")
}
func (i *noItemState) dispenseItem() error {
return fmt. Errorf ("out of stock")
}
vending_machine.go
package state
import "fmt"
type vendingMachine struct {
Hasitem state // there are goods
Itemrequested state // item requested
Hasmoney state // receipt of note
Noitem state // no product
Currentstate state // current state
Itemcount int // commodity price
Itemprice int // item inventory
}
func newVendingMachine(itemCount, itemPrice int) *vendingMachine {
v := &vendingMachine{
itemCount: itemCount,
itemPrice: itemPrice,
}
hasItemState := &hasItemState{
vendingMachine: v,
}
itemRequestedState := &itemRequestedState{
vendingMachine: v,
}
hasMoneyState := &hasMoneyState{
vendingMachine: v,
}
noItemState := &noItemState{
vendingMachine: v,
}
v.setState(hasItemState)
v.hasItem = hasItemState
v.itemRequested = itemRequestedState
v.hasMoney = hasMoneyState
v.noItem = noItemState
return v
}
func (v *vendingMachine) requestItem() error {
return v.currentState.requestItem()
}
func (v *vendingMachine) addItem(count int) error {
return v.currentState.addItem(count)
}
func (v *vendingMachine) insertMoney(money int) error {
return v.currentState.insertMoney(money)
}
func (v *vendingMachine) dispenseItem() error {
return v.currentState.dispenseItem()
}
func (v *vendingMachine) setState(s state) {
v.currentState = s
}
func (v *vendingMachine) incrementItemCount(count int) {
fmt. Printf ("inventory increased by% d items \ n", count)
v.itemCount = v.itemCount + count
}
example.go
package state
import (
"log"
)
func Example() {
vendingMachine := newVendingMachine(1, 10)
err := vendingMachine.requestItem()
if err != nil {
log.Fatalf(err.Error())
}
err = vendingMachine.insertMoney(10)
if err != nil {
log.Fatalf(err.Error())
}
err = vendingMachine.dispenseItem()
if err != nil {
log.Fatalf(err.Error())
}
err = vendingMachine.addItem(2)
if err != nil {
log.Fatalf(err.Error())
}
err = vendingMachine.requestItem()
if err != nil {
log.Fatalf(err.Error())
}
err = vendingMachine.insertMoney(10)
if err != nil {
log.Fatalf(err.Error())
}
err = vendingMachine.dispenseItem()
if err != nil {
log.Fatalf(err.Error())
}
}
//Operation results:
//Select product
//Shipping in progress
//Shipping in progress
//Inventory increased by 2 items
//Select product
//Shipping in progress
//Shipping in progress
advantage
- Single responsibility principle. Put the code related to a particular state in a separate class.
- Opening and closing principle. New states can be introduced without modifying existing state classes and contexts.
shortcoming
- The use of state mode will inevitably increase the number of classes and objects of the system.
- The structure and implementation of state mode are complex. Improper use will lead to confusion of program structure and code.