zoukankan      html  css  js  c++  java
  • GoLang设计模式14 状态模式

    状态模式,顾名思义,是一种基于有限状态机制的设计模式。在这种设计模式中,行为是由相应的状态来决定的。接下来我们会用一个售卖机的例子来说明下状态模式。为了便于说明,我们把场景简化一下,假设有一台售卖机只卖一种商品,且只有如下四种状态:

    1. 有商品
    2. 无商品
    3. 商品请求中
    4. 已收款

    一台售卖机也应该会有多种功能,我们同样做一下简化,假设这台售卖机只有四个功能:

    1. 选择商品
    2. 补充商品
    3. 投币
    4. 吐出商品

    什么时候使用状态模式

    • 在一个对象有多种不同状态的时候。对象需要根据当前的请求来改变它的状态
      • 在前面提到的例子中,售卖机将会受到行为的影响从一种状态切换到另一种状态。比如,当“投币”行为结束后,售货机将会从“商品请求中”状态切换到“已收款”状态。
    • 在一个对象需要根据它当前的状态对同一个请求做出不同响应的时候。这个时候使用状态模式可以避免大量的条件声明。
      • 仍然以售卖机为例,当用户想购买商品时,如果售卖机的状态为“有商品”,它就会继续处理,如果售卖机状态为“无商品”,它就会拒绝处理。请注意,这里售卖机根据它“有商品”和“无商品”的状态,对购买商品的请求作出了两种不同的响应。

    UML类图

    类图如下:

    代码

    看下代码:

    state.go:

    type state interface {
    	addItem(int) error
    
    	requestItem() error
    
    	insertMoney(money int) error
    
    	dispenseItem() error
    }
    

    这里简单解释下:

    在代码中我们定义了一个State接口,这个接口中有四个函数分别表示了售卖机的四种行为,如下:

    1. 购买商品:addItem(int) error
    2. 请求商品:requestItem() error
    3. 投币:insertMoney(money int) error
    4. 吐出商品:dispenseItem() error

    每个具体的状态实现都实现了以上四个函数,并对每种行为发生时该切换到哪种状态,以及如何响应做了处理

    每个具体的状态也都嵌入了一个指向当前售卖机的指针,这样以确保状态的切换是发生在这台售卖机上。

    vendingMachine.go:

    import "fmt"
    
    type vendingMachine struct {
    	hasItem       state
    	itemRequested state
    	hasMoney      state
    	noItem        state
    
    	currentState state
    
    	itemCount int
    	itemPrice int
    }
    
    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
    }
    

    注意这段代码,这里面没有任何条件表达式,所有逻辑处理均由相应的状态实现完成。

    下面是具体的状态实现。

    hasItemState.go:

    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")
    }
    

    hasMoneyState.go:

    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
    }
    

    itemRequestedState.go:

    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 {
    		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")
    }
    

    noItemState.go:

    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")
    }
    

    下面是场景实现main.go:

    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())
    	}
    }
    
    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
    }
    

    执行后输出为:

    Item requestd
    Money entered is ok
    Dispensing Item
    
    Adding 2 items
    
    Item requestd
    Money entered is ok
    Dispensing Item
    

    代码已上传至GitHub: zhyea / go-patterns / state-pattern

    End!!


    仅是学习笔记,难免出错,望不吝指点
  • 相关阅读:
    想出去走走
    一辈子的孤单
    好久不见
    bash 源码分析
    如何查看bash shell 帮助信息?
    mvn使用笔记
    StringTemplate初步使用
    使用tungsten将mysql的数据同步到hadoop
    《大脑幸福密码》
    《跳出头脑,融入生活》
  • 原文地址:https://www.cnblogs.com/amunote/p/15549886.html
Copyright © 2011-2022 走看看