状态模式(State Pattern)

未匹配的标注

问题场景:

糖果贩售机程序设计

问题描述:

状态模式(State Pattern)

状态模式(State Pattern)

未使用模式前可能编写的代码:

package state_pattern

import (
    "fmt"
    "strconv"
)

type CandyMachineTradition struct {
    status int
    count int
    coin int
}

const (
    SoldOut = 0
    NoCoin  = 1
    HasCoin = 2
)

// CoinOperated 投币
func (c *CandyMachineTradition)CoinOperated(){
    if c.status == NoCoin {
        if c.count>0{
            c.status = HasCoin
            c.coin++
            c.PrintStatus("success:投币成功!")
        }else{
            c.status = SoldOut
            c.PrintStatus("err:糖果已售罄,投币失败!")
        }
    }else if c.status== SoldOut {
        c.PrintStatus("err:糖果已售罄,投币失败!")
    }else if c.status== HasCoin {
        c.PrintStatus("err:当前有币,请先取糖!")
    }else {
        fmt.Println("err")
    }

}

// CoinReturn 退币
func (c *CandyMachineTradition)CoinReturn(){
    if c.status== HasCoin {
        c.status = NoCoin
        c.coin--
        c.PrintStatus("success:退币成功!")
    }else if c.status== SoldOut {
        fmt.Println("err:售罄状态,当前无币可退!")
    }else if  c.status== NoCoin{
        fmt.Println("err:未投币,当前无币可退!")
    }
}

// SugarExtraction 取糖
func (c *CandyMachineTradition)SugarExtraction(){
    if c.status== HasCoin {
        c.status = NoCoin
        c.count--
        c.PrintStatus("success:取糖成功!")
    }else if c.status== SoldOut {
        fmt.Println("err:售罄状态,取糖失败!")
    }else if  c.status== NoCoin{
        fmt.Println("err:未投币,取糖失败!")
    }
}

func (c *CandyMachineTradition)PrintStatus(msg string){
    fmt.Println(msg + " 当前有" + strconv.Itoa(c.coin) +"个币,有" + strconv.Itoa(c.count) +"个糖果")
}

func StartTradition(){
    fmt.Println("state-pattern | CandyMachineTradition")
    c := CandyMachineTradition{
        status: NoCoin,
        count:  1,
        coin:   0,
    }
    c.PrintStatus("设备初始化成功!")
    c.CoinOperated()
    c.CoinReturn()
    c.CoinOperated()
    c.SugarExtraction()

    c.CoinOperated()
    c.SugarExtraction()
    c.CoinReturn()
    c.CoinReturn()
}

输出

设备初始化成功! 当前有0个币,有1个糖果
success:投币成功! 当前有1个币,有1个糖果
success:退币成功! 当前有0个币,有1个糖果
success:投币成功! 当前有1个币,有1个糖果
success:取糖成功! 当前有1个币,有0个糖果
err:糖果已售罄,投币失败! 当前有1个币,有0个糖果
err:请先投币! 当前有1个币,有0个糖果
当前无币可退!
当前无币可退!

使用状态模式后编写的代码:

package state_pattern

import (
    "fmt"
    "strconv"
)

type CandyMachinePattern struct {
    status IStatus
    count int
    coin int
}
func (c *CandyMachinePattern)CoinOperated(){
    c.status.CoinOperated()
}
func (c *CandyMachinePattern)CoinReturn(){
    c.status.CoinReturn()
}
func (c *CandyMachinePattern)SugarExtraction(){
    c.status.SugarExtraction()
}
func (c *CandyMachinePattern)PrintStatus(msg string){
    fmt.Println(msg + " 当前有" + strconv.Itoa(c.coin) +"个币,有" + strconv.Itoa(c.count) +"个糖果")
}

// IStatus +-------------------------------------------------------
type IStatus  interface {
    CoinOperated()
    CoinReturn()
    SugarExtraction()
}
// SoldOutStatus +-------------------------------------------------------
type SoldOutStatus struct {
    c *CandyMachinePattern
}
func (s SoldOutStatus)CoinOperated(){
    s.c.PrintStatus("err:糖果已售罄,投币失败!")
}
func (s SoldOutStatus)CoinReturn(){
    s.c.PrintStatus("err:无币,退币失败!")
}
func (s SoldOutStatus)SugarExtraction(){
    s.c.PrintStatus("err:无币,退币失败!")
}
// NoCoinStatus +-------------------------------------------------------
type NoCoinStatus struct {
    c *CandyMachinePattern
}

func (s NoCoinStatus)CoinOperated(){
    s.c.coin++
    s.c.status = HasCoinStatus{
        c:s.c,
    }
    s.c.PrintStatus("success:投币成功!")
}
func (s NoCoinStatus)CoinReturn(){
    s.c.PrintStatus("err:无币可退!")
}
func (s NoCoinStatus)SugarExtraction(){
    s.c.PrintStatus("err:未投币,请先投币!")
}
// HasCoinStatus +-------------------------------------------------------
type HasCoinStatus struct {
    c *CandyMachinePattern
}

func (s HasCoinStatus)CoinOperated(){
    s.c.PrintStatus("err:已投币,请先取糖果!")
}
func (s HasCoinStatus)CoinReturn(){
    s.c.coin--
    s.c.status = NoCoinStatus{
        c:s.c,
    }
    s.c.PrintStatus("success:退币成功!")
}
func (s HasCoinStatus)SugarExtraction(){
    s.c.count--
    if s.c.count<=0{
        s.c.status = SoldOutStatus{
            c:s.c,
        }
    }else {
        s.c.status = NoCoinStatus{
            c:s.c,
        }
    }
    s.c.PrintStatus("success:取糖果成功!")
}


// StartPattern +-------------------------------------------------------
func StartPattern(){
    fmt.Println("state-pattern | CandyMachinePattern")
    c := CandyMachinePattern{
        count:  1,
        coin:   0,
    }
    c.status = NoCoinStatus{
        c:&c,
    }
    c.PrintStatus("设备初始化成功!")
    c.CoinOperated()
    c.CoinReturn()
    c.CoinOperated()
    c.SugarExtraction()

    c.CoinOperated()
    c.SugarExtraction()
    c.CoinReturn()
    c.CoinReturn()
}

使用场景:

  1. 行为随状态改变而改变的场景;
  2. 条件分支语句替代者;

与策略模式的区别:

  1. 状态模式重点在于各状态间切换从而做不同的事情;

  2. 策略模式侧重根据具体情况选择不同的策略做同一件事情(例如选择登录方式、支付方式),且不涉及切换;

  3. 状态模式封装了对象的状态;而策略模式封装算法或策略;

  4. 状态模式中,每个状态通过持有Context的引用,来实现状态转移;策略模式不持有Context的引用,只是被Context使用。

优点:

  1. 将所有的对象行为托管到状态类中,方便增加新的状态类,只需改变对象状态指向,即可改变对象行为;
  2. 避免了大量巨大的条件语句;

缺点:

  1. 对“开闭原则”支持不够好,切换进新状态需要修改其上游状态代码;
  2. 导致类增多,使用不当会导致程序结构和代码混乱;

参考资料:

  1. 状态模式 | 菜鸟教程 www.runoob.com/design-pattern/stat...
  2. 《Head First 设计模式》状态模式
  3. 【状态设计模式】使用场景 my.oschina.net/u/3995125/blog/3054...

本文章首发在 LearnKu.com 网站上。

上一篇 下一篇
讨论数量: 0
发起讨论 只看当前版本


暂无话题~