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

  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!!

GoLang设计模式14 - 状态模式的更多相关文章

  1. python设计模式之状态模式

    python设计模式之状态模式 面向对象编程着力于在对象交互时改变它们的状态.在很多问题中,有限状态机(通常名为状态机)是一个非常方便的状态转换建模(并在必要时以数学方式形式化)工具.首先,什么是状态 ...

  2. 【转】设计模式 ( 十七) 状态模式State(对象行为型)

    设计模式 ( 十七) 状态模式State(对象行为型) 1.概述 在软件开发过程中,应用程序可能会根据不同的情况作出不同的处理.最直接的解决方案是将这些所有可能发生的情况全都考虑到.然后使用if... ...

  3. 设计模式 ( 十七) 状态模式State(对象行为型)

    设计模式 ( 十七) 状态模式State(对象行为型) 1.概述 在软件开发过程中,应用程序可能会根据不同的情况作出不同的处理.最直接的解决方案是将这些所有可能发生的情况全都考虑到.然后使用if... ...

  4. 乐在其中设计模式(C#) - 状态模式(State Pattern)

    原文:乐在其中设计模式(C#) - 状态模式(State Pattern) [索引页][源码下载] 乐在其中设计模式(C#) - 状态模式(State Pattern) 作者:webabcd 介绍 允 ...

  5. 折腾Java设计模式之状态模式

    原文地址 折腾Java设计模式之状态模式 状态模式 在状态模式(State Pattern)中,类的行为是基于它的状态改变的.这种类型的设计模式属于行为型模式.在状态模式中,我们创建表示各种状态的对象 ...

  6. Golang设计模式—简单工厂模式(Simple Factory Pattern)

    Golang设计模式--简单工厂模式 背景 假设我们在做一款小型翻译软件,软件可以将德语.英语.日语都翻译成目标中文,并显示在前端. 思路 我们会有三个具体的语言翻译结构体,或许以后还有更多,但现在分 ...

  7. 北风设计模式课程---状态模式State(对象行为型)

    北风设计模式课程---状态模式State(对象行为型) 一.总结 一句话总结: 状态模式 具体状态的行为在具体的状态类中就解决,不用交给外部做判断.实质是将多条件判断弄成了多个类,在不同的类中做判断 ...

  8. js设计模式——5.状态模式

    js设计模式——5.状态模式 代码演示 /*js设计模式——状态模式*/ // 状态(红灯,黄灯,绿灯) class State { constructor(color) { this.color = ...

  9. 设计模式2——状态模式State

    参考链接: 设计模式之状态模式:https://www.cnblogs.com/haoerlv/p/7777789.html 设计模式系列之状态模式:https://www.jianshu.com/p ...

随机推荐

  1. isnull与ifnull适用数据库

    根据业务流程去查询某个数据表的某个字段的最大值: 直接用的select max(code) from base_area; 大多数情况没有问题,有个特殊点:如果数据表里边没有数据,且返回类型时int时 ...

  2. 技术与艺术的结合,HMS Core让手机主题趣味丛生

    在9月23日晚举办的华为nova9系列新品发布会上,华为在发布nova9系列新机之外,还为观众展示了多款Harmony OS趣味主题.其中一款名为"翻滚吧牛奶"的应用主题看起来十分 ...

  3. 关于突破 SESSION 0 隔离场景发现的一些问题

    0x00 Tricks 0x01 用ZwCreateThreadEx 在 Windows 10 下直接通过管理员权限+SeDebugPrivilege启用. 0x02 用CreateRemoteThr ...

  4. oracle dg failover灾难切换

    oracle dg failover灾难切换SQL> alter database recover managed standby database finish force;SQL> a ...

  5. Dapr + .NET Core实战(十一)单机Dapr集群负载均衡

    如何单机部署Dapr集群 第十篇讲过了K8S集群下如何使用Dapr运行程序,但是很多人一直在问如何单机下进行Dapr的负载,这节课我们来聊聊如何单机进行Dapr的负载. 首先要说的是单机下,通过 da ...

  6. JVM详解(四)——运行时数据区-堆

    一.堆 1.介绍 Java运行程序对应一个进程,一个进程就对应一个JVM实例.一个JVM实例就有一个运行时数据区(Runtime),Runtime里面,就只有一个堆,一个方法区.这里也阐述了,方法区和 ...

  7. Java(26)集合一Collection

    来源:季沐测试笔记 原文地址:https://www.cnblogs.com/testero/p/15228419.html 博客主页:https://www.cnblogs.com/testero ...

  8. dubbo注册中心占位符无法解析问题

    dubbo注册中心占位符无法解析问题 1.背景 最近搞了2个老项目,想把他们融合到一起.这俩项目情况简介如下: 项目一:基于SpringMVC + dubbo,配置读取本地properties文件,少 ...

  9. NX Open显示符号(UF_DISP_display_temporary_point)

    UF_DISP_display_temporary_point 使用方法: 1 Dim x As Double = 0, y As Double = 0, z As Double = 0 2 3 Di ...

  10. 5个步骤,教你瞬间明白线程和线程安全.md

    记得刚来杭州面试的时候,有一家公司的技术总监问了我这样一个问题:你来说说有哪些线程安全的类?我心里一想,这我早都背好了,稀里哗啦说了一大堆. 他又接着问:那你再来说说什么是线程安全?--然后我就GG了 ...