狀態模式是什麼 狀態模式是一種行為設計模式,讓你能在一個對象的內部狀態變化時改變其行為,使其看上去就像改變了自身所屬的類一樣。 為什麼要用狀態模式 如果對象需要根據自身當前狀態進行不同行為,同時狀態的數量非常多且與狀態相關的代碼會頻繁變更的話,可使用狀態模式。相似狀態和基於條件的狀態機轉換中存在許多 ...
狀態模式是什麼
狀態模式是一種行為設計模式,讓你能在一個對象的內部狀態變化時改變其行為,使其看上去就像改變了自身所屬的類一樣。
為什麼要用狀態模式
如果對象需要根據自身當前狀態進行不同行為,同時狀態的數量非常多且與狀態相關的代碼會頻繁變更的話,可使用狀態模式。相似狀態和基於條件的狀態機轉換中存在許多重覆代碼時,可使用狀態模式。
狀態模式怎麼實現
這裡用自動售貨機舉例,自動售貨機有四種狀態(有商品,商品已請求,收到紙幣,無商品)。而行為(選擇商品,增加商品,插入紙幣,出貨)會根據狀態的變化而變化。例如如果狀態為無商品時,不能選擇商品;而有商品時才可以選擇商品。
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 個商品
//選擇商品
//正在出貨
//正在出貨
優點
- 單一職責原則。 將與特定狀態相關的代碼放在單獨的類中。
- 開閉原則。 無需修改已有狀態類和上下文就能引入新狀態。
缺點
- 狀態模式的使用必然會增加系統的類與對象的個數。
- 狀態模式的結構與實現都較為複雜,如果使用不當會導致程式結構和代碼的混亂。