[go] 状态模式

简介: [go] 状态模式

状态模式


允许对象在内部状态改变时改变它的行为,对象看起来好像修改了它的类。


模型说明


https://pic4.58cdn.com.cn/nowater/webim/big/n_v2a8bbd22e4b0a43aabfc7ab4e94293461.png


  • 上下文 (Context) 保存了对于一个具体状态对象的引用, 并会将所有与该状态相关的工作委派给它。 上下文通过状态接口与状态对象交互, 且会提供一个设置器用于传递新的状态对象。
  • 状态 (State) 接口会声明特定于状态的方法。 这些方法应能被其他所有具体状态所理解, 因为你不希望某些状态所拥有的方法永远不会被调用。
  • 具体状态 (Concrete States) 会自行实现特定于状态的方法。 为了避免多个状态中包含相似代码, 你可以提供一个封装有部分通用行为的中间抽象类。
  • 状态对象可存储对于上下文对象的反向引用。 状态可以通过该引用从上下文处获取所需信息, 并且能触发状态转移。
  • 上下文和具体状态都可以设置上下文的下个状态, 并可通过替换连接到上下文的状态对象来完成实际的状态转换。


优缺点


1.优点


  • 单一职责原则: 将与特定状态相关的代码放在单独的类中。
  • 开闭原则: 无需修改已有状态类和上下文就能引入新状态。
  • 通过消除臃肿的状态机条件语句简化上下文代码。


2.缺点


  • 如果状态机只有很少的几个状态,或者很少发生改变,那么应用该模式可能会显得小题大作。


使用场景

  • 如果对象需要根据自身当前状态进行不同行为,同时状态的数量非常多且与状态相关的代码会频繁变更的话,可使用状态模式。
  • 如果某个类需要根据成员变量的当前值改变自身行为,从而需要使用大量的条件语句时,可使用该模式。
  • 当相似状态和基于条件的状态机转换中存在许多重复代码时,可使用状态模式。


参考代码


一台自动售货机上使用状态设计模式:

  • 有商品 (has­Item)
  • 无商品 (no­Item)
  • 商品已请求 (item­Requested)
  • 收到纸币 (has­Money)


同时, 自动售货机也会有不同的操作。 再一次的, 为了简单起见, 我们假设其只会执行 4 种操作:


  • 选择商品
  • 添加商品
  • 插入纸币
  • 提供商品


// vendingMachine.go 自动售货机
package main
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("Adding %d items\n", count)
 v.itemCount = v.itemCount + count
}
// state.go 状态接口
package main
type State interface {
 addItem(int) error
 requestItem() error
 insertMoney(money int) error
 dispenseItem() error
}
// noItemState.go 无商品状态
package main
import "fmt"
type NoItemState struct {
    vendingMachine *VendingMachine
}
func (i *NoItemState) requestItem() error {
    return fmt.Errorf("Item 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("Item out of stock")
}
func (i *NoItemState) dispenseItem() error {
    return fmt.Errorf("Item out of stock")
}
// hasItemState.go 有商品状态
package main
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("No item present")
 }
 fmt.Printf("Item requestd\n")
 i.vendingMachine.setState(i.vendingMachine.itemRequested)
 return nil
}
func (i *HasItemState) addItem(count int) error {
 fmt.Printf("%d items added\n", count)
 i.vendingMachine.incrementItemCount(count)
 return nil
}
func (i *HasItemState) insertMoney(money int) error {
 return fmt.Errorf("Please select item first")
}
func (i *HasItemState) dispenseItem() error {
 return fmt.Errorf("Please select item first")
}
// itemRequestedState.go 请求商品
package main
import "fmt"
type ItemRequestedState struct {
 vendingMachine *VendingMachine
}
func (i *ItemRequestedState) requestItem() error {
 return fmt.Errorf("Item already requested")
}
func (i *ItemRequestedState) addItem(count int) error {
 return fmt.Errorf("Item Dispense in progress")
}
func (i *ItemRequestedState) insertMoney(money int) error {
 if money < i.vendingMachine.itemPrice {
  return fmt.Errorf("Inserted money is less. Please insert %d", i.vendingMachine.itemPrice)
 }
 fmt.Println("Money entered is ok")
 i.vendingMachine.setState(i.vendingMachine.hasMoney)
 return nil
}
func (i *ItemRequestedState) dispenseItem() error {
 return fmt.Errorf("Please insert money first")
}
// hasMoneyState.go 收到钱
package main
import "fmt"
type HasMoneyState struct {
 vendingMachine *VendingMachine
}
func (i *HasMoneyState) requestItem() error {
 return fmt.Errorf("Item dispense in progress")
}
func (i *HasMoneyState) addItem(count int) error {
 return fmt.Errorf("Item dispense in progress")
}
func (i *HasMoneyState) insertMoney(money int) error {
 return fmt.Errorf("Item out of stock")
}
func (i *HasMoneyState) dispenseItem() error {
 fmt.Println("Dispensing Item")
 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
}
// main.go 客户端
package main
import (
 "fmt"
 "log"
)
func main() {
 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())
 }
 fmt.Println()
 err = vendingMachine.addItem(2)
 if err != nil {
  log.Fatalf(err.Error())
 }
 fmt.Println()
 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())
 }
}


output:


Item requestd
Money entered is ok
Dispensing Item
Adding 2 items
Item requestd
Money entered is ok
Dispensing Item
相关文章
|
3月前
|
Go 容器
|
3月前
|
设计模式 存储 Go
|
3月前
|
缓存 算法 Go
|
3月前
|
设计模式 监控 Go
|
3月前
|
设计模式 算法 Go
|
3月前
|
缓存 算法 Go
|
3月前
|
存储 设计模式 缓存
|
3月前
|
设计模式 Go
|
3月前
|
算法 Go