行为型:九. 状态模式
状态模式是什么
状态模式是一种行为设计模式,让你能在一个对象的内部状态变化时改变其行为,使其看上去就像改变了自身所属的类一样。
为什么要用状态模式
如果对象需要根据自身当前状态进行不同行为,同时状态的数量非常多且与状态相关的代码会频繁变更的话,可使用状态模式。相似状态和基于条件的状态机转换中存在许多重复代码时,可使用状态模式。
状态模式怎么实现
这里用自动售货机举例,自动售货机有四种状态(有商品,商品已请求,收到纸币,无商品)。而行为(选择商品,增加商品,插入纸币,出货)会根据状态的变化而变化。例如如果状态为无商品时,不能选择商品;而有商品时才可以选择商品。
state.go
package state
type state interface {
addItem(int) error // 添加商品
requestItem() error // 选择商品
insertMoney(money int) error // 付钱
dispenseItem() error // 提供商品
}
has_item_state.go
package state
import "fmt"
// 有商品状态
type hasItemState struct {
vendingMachine *vendingMachine
}
func (i *hasItemState) requestItem() error {
if i.vendingMachine.itemCount == 0 {
i.vendingMachine.setState(i.vendingMachine.noItem)
return fmt.Errorf("没有库存")
}
fmt.Printf("选择商品\n")
i.vendingMachine.setState(i.vendingMachine.itemRequested)
return nil
}
func (i *hasItemState) addItem(count int) error {
fmt.Printf("%d 增加商品数量:\n", count)
i.vendingMachine.incrementItemCount(count)
return nil
}
func (i *hasItemState) insertMoney(money int) error {
return fmt.Errorf("请先选择商品")
}
func (i *hasItemState) dispenseItem() error {
return fmt.Errorf("请先选择商品")
}
has_money_state.go
package state
import "fmt"
// 收到纸币
type hasMoneyState struct {
vendingMachine *vendingMachine
}
func (i *hasMoneyState) requestItem() error {
return fmt.Errorf("正在出货,请等待")
}
func (i *hasMoneyState) addItem(count int) error {
return fmt.Errorf("正在出货,请等待")
}
func (i *hasMoneyState) insertMoney(money int) error {
return fmt.Errorf("正在出货,请等待")
}
func (i *hasMoneyState) dispenseItem() error {
fmt.Println("正在出货")
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"
)
// 商品已选择状态
type itemRequestedState struct {
vendingMachine *vendingMachine
}
func (i *itemRequestedState) requestItem() error {
return fmt.Errorf("商品已经选择")
}
func (i *itemRequestedState) addItem(count int) error {
return fmt.Errorf("正在售卖中,不能添加商品")
}
func (i *itemRequestedState) insertMoney(money int) error {
if money < i.vendingMachine.itemPrice {
fmt.Errorf("插入金额不足,请插入: %d", i.vendingMachine.itemPrice)
return errors.New("插入金额不足")
}
fmt.Println("正在出货")
i.vendingMachine.setState(i.vendingMachine.hasMoney)
return nil
}
func (i *itemRequestedState) dispenseItem() error {
return fmt.Errorf("请先投币")
}
no_item_state.go
package state
import "fmt"
// 无商品状态
type noItemState struct {
vendingMachine *vendingMachine
}
func (i *noItemState) requestItem() error {
return fmt.Errorf("库存不足")
}
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("库存不足")
}
func (i *noItemState) dispenseItem() error {
return fmt.Errorf("库存不足")
}
vending_machine.go
package state
import "fmt"
type vendingMachine struct {
hasItem state // 有商品
itemRequested state // 商品已请求
hasMoney state // 收到纸币
noItem state // 无商品
currentState state // 当前状态
itemCount int // 商品价格
itemPrice int // 商品库存
}
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("库存增加 %d 个商品\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())
}
}
//运行结果:
//选择商品
//正在出货
//正在出货
//库存增加 2 个商品
//选择商品
//正在出货
//正在出货
优点
- 单一职责原则。 将与特定状态相关的代码放在单独的类中。
- 开闭原则。 无需修改已有状态类和上下文就能引入新状态。
缺点
- 状态模式的使用必然会增加系统的类与对象的个数。
- 状态模式的结构与实现都较为复杂,如果使用不当会导致程序结构和代码的混乱。