【设计模式】16、state 状态模式

文章目录

  • 十六、state 状态模式
    • 16.1 自动购物机
      • 16.1.1 vending_machine_test.go
      • 16.1.2 vending_maching.go
      • 16.1.3 state.go
      • 16.1.4 no_good_state.go
      • 16.1.5 has_good_state.go
    • 16.2 player
      • 16.2.1 player_test.go
      • 16.2.2 player.go
      • 16.2.3 state.go
      • 16.2.4 stopped_state.go
      • 16.2.5 playing_state.go

十六、state 状态模式

https://refactoringguru.cn/design-patterns/state

在不同的情况下, 执行对应的操作. 通常是由 if else 实现的. 但随着需求扩张, 代码无法维护

可以描述出各种状态(即各种 if 的条件), 把状态切换的控制流, 和状态的具体操作的业务流, 拆分开.

通常 Context 类持有 state 接口, state 接口有很多实现

每种 state 的实现, 都持有 Context 类的反向引用用于切换状态, 并只负责当前状态需执行的操作

16.1 自动购物机

https://refactoringguru.cn/design-patterns/state/go/example

自动购物机, 包括如下状态

  1. 初始为无货状态, 当供应商添加货物后, 变为有货状态
  2. 当用户选择货物后, 变为已选择货物的状态
  3. 当用户付款后, 售卖货物, 并货物数量减少, 最终回到有货状态
├── has_good_state.go
├── no_good_state.go
├── readme.md
├── state.go
├── vending_machine_test.go
└── vending_maching.go

16.1.1 vending_machine_test.go

package _61vending_machingimport ("fmt""github.com/stretchr/testify/require""testing"
)/*
=== RUN   TestVendingMachine
---case1---
[noGoodState] SelectGood start, 但因无货而无法选择货物
---case2---
[noGoodState] AddGood start
[添加商品] 开始
[添加商品] 成功
[noGoodState] AddGood success
[切换为有货状态] 开始
[切换为有货状态] 结束
[hasGoodState] SelectGood start
[选择商品] 开始
[选择商品] 成功
[hasGoodState] SelectGood success
[hasGoodState] Buy start
[购买] 开始
[pay] 开始
[pay] 成功
[移除商品] 开始
[移除商品] 成功
[购买] 成功
[hasGoodState] Buy success
[切换为无货状态] 开始
[切换为无货状态] 结束
--- PASS: TestVendingMachine (0.00s)
PASS
*/
func TestVendingMachine(t *testing.T) {m := NewVendingMachine()const good1 = "煎饼果子"fmt.Println("---case1---")m.SelectGood(good1)require.Len(t, m.goods, 0)fmt.Println("---case2---")m.AddGood(good1)require.Len(t, m.goods, 1)require.Empty(t, m.selectedGood)m.SelectGood(good1)require.NotEmpty(t, m.selectedGood)m.Buy()require.Len(t, m.goods, 0)
}

16.1.2 vending_maching.go

package _61vending_machingimport "fmt"// 购物机
type vendingMachine struct {state        stategoods        map[string]struct{} // 所有商品列表selectedGood string              // 当前选中的商品
}func NewVendingMachine() *vendingMachine {m := &vendingMachine{state:        nil, // 后续填充goods:        make(map[string]struct{}),selectedGood: "",}m.state = NewNoGoodState(m) // state 反向引用 contextreturn m
}// 切换状态
func (m *vendingMachine) changeState(s state) {m.state = s
}func (m *vendingMachine) AddGood(g string) {m.state.AddGood(g)
}func (m *vendingMachine) SelectGood(g string) {m.state.SelectGood(g)
}func (m *vendingMachine) Buy() {m.state.Buy()
}// ------- 私有方法 ------
func (m *vendingMachine) addGood(g string) {fmt.Println("[添加商品] 开始")_, exist := m.goods[g]if exist {fmt.Println("[添加商品] 已存在, 无需添加")return}m.goods[g] = struct{}{}fmt.Println("[添加商品] 成功")
}func (m *vendingMachine) selectGood(g string) error {fmt.Println("[选择商品] 开始")_, exist := m.goods[g]if !exist {return fmt.Errorf("[选择商品] 并不存在, 无法选择")}m.selectedGood = gfmt.Println("[选择商品] 成功")return nil
}func (m *vendingMachine) buy() {fmt.Println("[购买] 开始")m.pay()m.removeGood(m.selectedGood)m.selectedGood = ""fmt.Println("[购买] 成功")
}func (m *vendingMachine) pay() {fmt.Println("[pay] 开始")fmt.Println("[pay] 成功")
}func (m *vendingMachine) removeGood(g string) {fmt.Println("[移除商品] 开始")_, exist := m.goods[g]if !exist {fmt.Println("[移除商品] 并不存在, 无需移除")return}delete(m.goods, g)fmt.Println("[移除商品] 成功")
}

16.1.3 state.go

package _61vending_maching// 每种 state 都有如下操作
type state interface {// AddGood 添加商品AddGood(g string)// SelectGood 选择商品SelectGood(g string)// Buy 付款Pay+取货RemoveGoodBuy()
}

16.1.4 no_good_state.go

package _61vending_machingimport "fmt"type noGoodState struct {m *vendingMachine
}func NewNoGoodState(m *vendingMachine) state {return &noGoodState{m: m}
}func (s *noGoodState) AddGood(g string) {fmt.Println("[noGoodState] AddGood start")s.m.addGood(g)fmt.Println("[noGoodState] AddGood success")fmt.Println("[切换为有货状态] 开始")s.m.changeState(NewHasGoodState(s.m))fmt.Println("[切换为有货状态] 结束")
}func (s *noGoodState) SelectGood(g string) {fmt.Println("[noGoodState] SelectGood start, 但因无货而无法选择货物")
}func (s *noGoodState) Buy() {fmt.Println("[noGoodState] Buy start, 但因无货而无法购买")
}

16.1.5 has_good_state.go

package _61vending_machingimport "fmt"type hasGoodState struct {m *vendingMachine
}func NewHasGoodState(m *vendingMachine) state {return &hasGoodState{m: m}
}func (s *hasGoodState) AddGood(g string) {fmt.Println("[hasGoodState] AddGood start")s.m.addGood(g)fmt.Println("[hasGoodState] AddGood success")
}func (s *hasGoodState) SelectGood(g string) {fmt.Println("[hasGoodState] SelectGood start")s.m.selectGood(g)fmt.Println("[hasGoodState] SelectGood success")
}func (s *hasGoodState) Buy() {fmt.Println("[hasGoodState] Buy start")s.m.buy()fmt.Println("[hasGoodState] Buy success")if len(s.m.goods) == 0 {fmt.Println("[切换为无货状态] 开始")s.m.changeState(NewHasGoodState(s.m))fmt.Println("[切换为无货状态] 结束")}
}

16.2 player

音乐播放器, 有 Stopped, Playing 两种状态

https://refactoringguru.cn/design-patterns/state/rust/example

├── player.go
├── player_test.go
├── playing_state.go
├── readme.md
├── state.go
└── stopped_state.go

16.2.1 player_test.go

package _62playerimport "testing"/*
=== RUN   TestPlayer
在 暂停状态, 点击暂停按钮后, 无效果
在 暂停状态, 点击播放按钮后, 切换为播放状态
[切换状态] 暂停状态 => 播放状态
在 播放状态, 点击暂停按钮后, 切换为暂停状态
[切换状态] 播放状态 => 暂停状态
--- PASS: TestPlayer (0.00s)
PASS
*/
func TestPlayer(t *testing.T) {p := NewPlayer()p.Stop()p.Play()p.Stop()
}

16.2.2 player.go

package _62playerimport "fmt"type player struct {state state
}func NewPlayer() *player {p := &player{}s := NewStoppedState(p) // state 持有 Context 的反向引用p.state = s             // Context 持有 statereturn p
}func (p *player) ChangeState(s state) {fmt.Printf("[切换状态] %v => %v\n", p.state.GetStateName(), s.GetStateName())p.state = s
}func (p *player) Play() {p.state.Play()
}func (p *player) Stop() {p.state.Stop()
}

16.2.3 state.go

package _62playertype state interface {Play()Stop()GetStateName() string
}

16.2.4 stopped_state.go

package _62playerimport "fmt"type StoppedState struct {player *player
}func NewStoppedState(player *player) state {return &StoppedState{player: player}
}func (s *StoppedState) Play() {fmt.Printf("在 %v, 点击播放按钮后, 切换为播放状态\n", s.GetStateName())s.player.ChangeState(NewPlayingState(s.player))
}func (s *StoppedState) Stop() {fmt.Printf("在 %v, 点击暂停按钮后, 无效果\n", s.GetStateName())
}func (s *StoppedState) GetStateName() string {return "暂停状态"
}

16.2.5 playing_state.go

package _62playerimport "fmt"type PlayingState struct {player *player
}func NewPlayingState(player *player) state {return &PlayingState{player: player}
}func (s *PlayingState) Play() {fmt.Printf("在 %v, 点击播放按钮后, 无效果\n", s.GetStateName())
}func (s *PlayingState) Stop() {fmt.Printf("在 %v, 点击暂停按钮后, 切换为暂停状态\n", s.GetStateName())s.player.ChangeState(NewStoppedState(s.player))
}func (s *PlayingState) GetStateName() string {return "播放状态"
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/5976.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

go的grpc的三种流模式通信

go的grpc的三种流模式通信 1、grpc通信模式简介2、stream.proto文件3、服务端代码 server.go4、客户端代码client.go5、测试说明 1、grpc通信模式简介 grpc的数据传输可以分为4种模式: 简单模式 (一元调用) 服务端流模式 (服务端返回实时股票数据给前台) 客户端流模…

kubernetes中使用ELK进行日志收集

目录 一、需要收集哪些日志 1、kubernetes集群的系统组件日志 2、应用日志 二、日志收集方案ELK 1、收集日志:Logstash 2、存储日志:Elasticsearch 3、展示日志:Kibana 三、安装elk 1、下载安装包 2、创建用户并切换到新用户 3、上…

npm许可证检查

node开发做项目,很少有人去纯手工打造,大多是采用一些开源框架,还会使用前人做好的轮子,所以咱们的项目文件里,除了自己编写的js文件,还会带有一些拿来主义的npm模块,从其他开源发布网站上下载的…

2024-05-02 商业分析-杭州小万科技-商业模式分析

摘要: 对杭州小万科技的商业模式进行分析,以对其做出客观的评估。 杭州小万科技的资料: 杭州小万科技有限公司 - 企知道 (qizhidao.com) 杭州小万科技有限公司网站备案查询 - 天眼查 (tianyancha.com) 杭州小万科技有限公司 - 爱企查 (baidu.com) ​ 2023年年报:

SMB 协议详解之-TreeID原理和SMB数据包分析技巧

在前面分析SMB协议数据包的过程中,这里,可以看到在SMB协议中存在很多的ID,即Unique Identifiers。那么这些ID表示什么含义?在实际分析数据包的过程中如何根据这些ID进行过滤分析?本文将介绍SMB/SMB2中的tree id ,并介绍如何通过tree id 快速的分析SMB数据包中各种命令交互…

Django响应‘表单请求’过程

(1)用户通过自己的浏览器(客户端)第一次向服务器发出含有表单页面的请求,Django会创建一个未绑定数据的表单实例(例如form LoginForm(), form实例就是未绑定实例),即空表单&#xf…

现代JavaScript:对ES6+的深入讲解,新型的JS特性以及怎样在项目中使用它们

现代JavaScript,也就是ES6(ECMAScript 6)和更高版本,引入了很多新的语言特性来增强JavaScript的编程能力。以下为一些关键的新特性及其在项目中的使用: 1、let 和 const 关键字: 在ES6之前,我们…

使用jdbc方式操作ClickHouse

1、创建测试表,和插入测试数据 create table t_order01(id UInt32,sku_id String,total_amount Decimal(16,2),create_time Datetime ) engine MergeTreepartition by toYYYYMMDD(create_time)primary key (id)order by (id,sku_id);insert into t_order01 values …

MR混合现实情景实训教学系统在商务接待课堂上的应用

随着科技的不断发展,MR混合现实情景实训教学系统已经逐渐应用于商务接待课堂。这种新型的实训教学系统将虚拟现实技术与现实环境相结合,为商务接待课堂带来了全新的教学方式和体验。 首先,MR混合现实情景实训教学系统能够为学生提供真实的商务…

Agent AI智能体:如何借助机器学习引领科技新潮流

文章目录 📑前言一、Agent AI智能体的基本概念二、Agent AI智能体的技术进步2.1 机器学习技术2.2 自适应技术2.3 分布式计算与云计算 三、Agent AI智能体的知识积累3.1 知识图谱3.2 迁移学习 四、Agent AI智能体的挑战与机遇4.1 挑战4.2 机遇 小结 📑前言…

App异常汇总与对策

UI交互异常 空显示/白屏 一般是因为数据为空或获取失败。要请产品定义加载中、加载失败、数据为空的UI。显示不完整、错位 开发时考虑不同屏幕大小、窗体大小、内容量的兼容,做好对齐和层级的设置。内容量会引起折行、显示不全等问题。如果有改变字体大小或多语言设…

Java基础(10)反射

Java反射是Java语言中的一个功能强大且复杂的机制,它允许程序在运行时访问、检查和修改它本身的结构(类、接口、字段、方法等)。反射机制主要在java.lang.reflect包中定义。 反射的核心组件 Class类:它的实例表示正在运行的Java…

【网站项目】高校推免报名

🙊作者简介:拥有多年开发工作经验,分享技术代码帮助学生学习,独立完成自己的项目或者毕业设计。 代码可以私聊博主获取。🌹赠送计算机毕业设计600个选题excel文件,帮助大学选题。赠送开题报告模板&#xff…

K8s: Kubernetes扩展之自定义资源

自定义资源 自定义资源是 K8s 的扩展,有时候需要对K8s进行一个扩展在默认的K8s集群里面提供的资源对象是一个有限的集合比如常用的pod, deployment, service,这些都是K8s原生的资源之所以它资源,是因为它能够对外提供API接口变成一个resourc…

Java-异常处理-定义三角形类Triangle和异常三角形IllegalTriangleException类 (1/2)

任意一个三角形,其任意两边之和大于第三边。当三角形的三条边不满足前述条件时,就表示发生了异常,将这种异常情况定义为IllegalTriangleException类。 自定义异常类IllegalTriangleException: 当三角形的三条边不满足条件&#x…

[随记]Mac安装Docker及运行开源Penpot

下载Docker Desktop for Mac:https://www.docker.com/products/docker-desktop/ 安装Docker Desktop for Mac,安装完成后,启动Docker,然后在终端输入: docker version 在Mac电脑的Desktop,随便创建一个文…

零代码编程:用通义千问免费批量翻译英文文档

首先,在阿里云的dashScope灵积模型服务中,申请一个API-key,有挺多免费token的。 然后,在通义千问中输入提示词: 你是一个Python编程专家,现在要完成一个编写基于qwen-turbo模型API和dashscope库的程序脚本…

UML图(总结)

一、静态建模 1、类图: 展现了一组对象、接口、协作和它们之间的关系。 2、对象图 展现了某一时刻一组对象以及它们之间的关系。 3、用例图 展现了用例、参与者(Action)以及它们之间的关系。 二、动态建模 1、序列图(顺序图,时序图) 描述了以…

Android Handler用法

Android Handler用法 为什么要设计Handler机制?Handler的用法1、创建Handler2、Handler通信2.1 sendMessage 方式2.2 post 方式 Handler常用方法1、延时执行2、周期执行 HandlerThread用法主线程-创建Handler子线程-创建Handler FAQMessage是如何创建主线程中Looper…

云端芳华、运维之美

今天,在我们享受互联网服务带来的便利与高效的同时,有一群人默默地在幕后为我们提供支持,他们就是云端运维人员。 值此五一国际劳动节来临之际,我们要真诚感谢他们辛勤的劳动和奉献!