Behavior type: IX State mode

Time:2022-5-7

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

  1. Single responsibility principle. Put the code related to a particular state in a separate class.
  2. Opening and closing principle. New states can be introduced without modifying existing state classes and contexts.

shortcoming

  1. The use of state mode will inevitably increase the number of classes and objects of the system.
  2. The structure and implementation of state mode are complex. Improper use will lead to confusion of program structure and code.

Recommended Today

Chapter 45 SQL command from (I)

Chapter 45 SQL command from (I) A select clause that specifies one or more tables to query. outline SELECT … FROM [optimize-option] table-ref [[AS] t-alias][,table-ref [[AS] t-alias]][,…] parameter optimize-optioN – optional – specifies a single keyword or a series of keywords separated by spaces for query optimization options (optimizer tips). The following keywords are supported:%ALLINDEX、%FIRSTTABLE […]