状态模式(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()
}
使用场景:
- 行为随状态改变而改变的场景;
- 条件分支语句替代者;
与策略模式的区别:
状态模式重点在于各状态间切换从而做不同的事情;
策略模式侧重根据具体情况选择不同的策略做同一件事情(例如选择登录方式、支付方式),且不涉及切换;
状态模式封装了对象的状态;而策略模式封装算法或策略;
状态模式中,每个状态通过持有Context的引用,来实现状态转移;策略模式不持有Context的引用,只是被Context使用。
优点:
- 将所有的对象行为托管到状态类中,方便增加新的状态类,只需改变对象状态指向,即可改变对象行为;
- 避免了大量巨大的条件语句;
缺点:
- 对“开闭原则”支持不够好,切换进新状态需要修改其上游状态代码;
- 导致类增多,使用不当会导致程序结构和代码混乱;
参考资料:
- 状态模式 | 菜鸟教程 www.runoob.com/design-pattern/stat...
- 《Head First 设计模式》状态模式
- 【状态设计模式】使用场景 my.oschina.net/u/3995125/blog/3054...