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

  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. Jmeter压测学习2---提取token,并关联参数

    注意:我是根据我司的项目写的,这里作为一个笔记使用,不要照搬. 一般登录操作,都会有个token,我们要提取token作为参数,用于后面的操作. 接口的登录是返回一个json数据,token值在返回的 ...

  2. Fortran学习笔记:01 基本格式与变量声明

    Fortran学习笔记目录 01 基本格式与变量声明 格式 固定格式(Fixed Format):Fortran77 程序需要满足一种特定的格式要求,具体形式参考教材 自由格式(Free Format ...

  3. JVM学习笔记——堆

    堆 Heap 一个 JVM 只有一个堆,堆也是 Java 内存管理的核心区域.在 JVM 启动时堆被创建,同时大小在启动时已设定好,堆是 JVM 管理最大的一块内存空间,其大小可以调节. 堆的内存空间 ...

  4. qsort()函数的使用

    函数声明 void qsort(void *base, size_t nitems, size_t size, int (*compar)(const void *, const void*)) 参数 ...

  5. SpringBoot-自动装配2

    配置文件到底能写什么?怎么写? SpringBoot官方文档中有大量的配置,直接去记忆的话,好像不是我们程序员的行事风格! 分析自动配置原理 能自动配置的组件一般都有命名为下面规则的两个类: xxxx ...

  6. 初学python-day3 列表

  7. 80. 删除有序数组中的重复项 II

    题目 给你一个有序数组 nums ,请你原地删除重复出现的元素(不需要考虑数组中超出新长度后面的元素),使每个元素最多出现两次 ,返回删除后数组的新长度. 不要使用额外的数组空间,你必须在原地修改输入 ...

  8. Java:动态代理小记

    Java:动态代理小记 对 Java 中的 动态代理,做一个微不足道的小小小小记 概述 动态代理:当想要给实现了某个接口的类中的方法,加一些额外的处理.比如说加日志,加事务等.可以给这个类创建一个代理 ...

  9. 普通用户在命令终端使用Python脚本连入校园网

    普通用户在命令终端使用Python脚本连入校园网 想要连入校园网的步骤: 浏览器输入对应的IP地址,输入账号密码连网: 下载对应软件,输入账号密码连网: 而面对没有界面的服务器,而你又没有root权限 ...

  10. DDL_Killer Alpha版本 Bug集中反馈处

    本博客用于DDL_Killer Alpha版本的Bug集中反馈. 您可以在本博客的下方评论区处留言,反馈您在使用DDl_Killer的过程中遇到的问题,以帮助我们更好的改进本产品. 我们会尽快修复找到 ...