【设计模式】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_maching

import (
    "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_maching

import "fmt"

// 购物机
type vendingMachine struct {
    state        state
    goods        map[string]struct{} // 所有商品列表
    selectedGood string              // 当前选中的商品
}

func NewVendingMachine() *vendingMachine {
    m := &vendingMachine{
        state:        nil, // 后续填充
        goods:        make(map[string]struct{}),
        selectedGood: "",
    }
    m.state = NewNoGoodState(m) // state 反向引用 context
    return 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 = g
    fmt.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+取货RemoveGood
    Buy()
}

16.1.4 no_good_state.go

package _61vending_maching

import "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_maching

import "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 _62player

import "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 _62player

import "fmt"

type player struct {
	state state
}

func NewPlayer() *player {
	p := &player{}
	s := NewStoppedState(p) // state 持有 Context 的反向引用
	p.state = s             // Context 持有 state
	return 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 _62player

type state interface {
	Play()
	Stop()
	GetStateName() string
}

16.2.4 stopped_state.go

package _62player

import "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 _62player

import "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.mfbz.cn/a/589480.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

OpenVINO安装教程 Docker版

从 Docker 映像安装IntelDistribution OpenVINO™ 工具套件 本指南介绍了如何使用预构建的 Docker 镜像/手动创建镜像来安装 OpenVINO™ Runtime。 Docker Base 映像支持的主机操作系统: Linux操作系统 Windows (WSL2) macOS(仅限 CPU exectuion) 您可以使用预…

【跟马少平老师学AI】-【神经网络是怎么实现的】(八)循环神经网络

一句话归纳: 1)词向量与句子向量的循环神经网络: x(i)为词向量。h(i)为含前i个词信息的向量。h(t)为句向量。 2)循环神经网络的局部。 每个子网络都是标准的全连接神经网络。 3)对句向量增加全连接层和激活函数。 每个…

I2C接口18路LED呼吸灯驱动IS31FL3218互相替代SN3218替换HTR3218

I2C接口18路LED呼吸灯控制电路IC 该型号IC为QFN24接口,属于小众产品,IS31FL3218、SN3218、HTR3218S管脚兼容,需要注意的是HTR3218管脚与其他型号不兼容。 I2C接口可实现多个LED灯的呼吸灯控制,可实现单色控制18个LED灯&#xff0…

【ARM Cache 系列文章 11.2 -- ARM Cache 组相联映射】

请阅读【ARM Cache 系列文章专栏导读】 文章目录 Cache 组相联映射组相联映射原理多路组相连缓存的优势多路组相连缓存的代价关联度(Associativity) 上篇文章:【ARM Cache 系列文章 11.1 – ARM Cache 全相连 详细介绍】 Cache 组相联映射 A…

笔记1--Llama 3 超级课堂 | Llama3概述与演进历程

1、Llama 3概述 https://github.com/SmartFlowAI/Llama3-Tutorial.git 【Llama 3 五一超级课堂 | Llama3概述与演进历程】 2、Llama 3 改进点 【最新【大模型微调】大模型llama3技术全面解析 大模型应用部署 据说llama3不满足scaling law?】…

Deep learning Part Five RNN--24.4.29

接着上期,CBOW模型无法解决文章内容过长的单词预测的,那该如何解决呢? 除此之外,根据图中5-5的左图所示,在CBOW模型的中间层求单词向量的和,这时就会出现另一个问题的,那就是上下文的单词的顺序…

Redis Zset的底层原理

Redis Zset的底层原理 ZSet也就是SortedSet,其中每一个元素都需要指定一个score值和member值: 可以根据score值排序后member必须唯一可以根据member查询分数 因此,zset底层数据结构必须满足键值存储、键必须唯一、可排序这几个需求。之前学…

ZooKeeper知识点总结及分布式锁实现

最初接触ZooKeeper是之前的一个公司的微服务项目中,涉及到Dubbo和ZooKeeper,ZooKeeper作为微服务的注册和配置中心。好了,开始介绍ZooKeeper了。 目录 1.ZooKeeper的基本概念 2.ZooKeeper的节点(ZNode) 3. ZooKeep…

【Java笔记】第5章:函数

前言1. 函数的理解2. 函数的基本使用3. 函数的参数4. 函数的返回值5. 函数的执行机制6. 函数的递归调用结语 ↓ 上期回顾: 【Java笔记】第4章:深入学习循环结构 个人主页:C_GUIQU 归属专栏:【Java学习】 ↑ 前言 各位小伙伴大家好&#xff…

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

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

【真实体验】使用崖山YMP 迁移 Oracle/MySQL 至YashanDB 23.2 验证测试【YashanDB迁移体验官】

一、前言 说一下我和崖山数据库的结缘,大概在去年吧,因为我经常在墨天轮写文章,看到崖山数据库推出了一崖山体验官的活动,我就报名参加了。第一次体验了崖山数据库,也测试了我司数据库到崖山数据库的兼容性&#xff0…

钉钉手机端调试前端H5项目流程

此流程以Vue项目为例 一、操作步骤 在根目录下 vue.config.js 文件中将 devServer.host 设置为 0.0.0.0 // vue.config.js module.exports {devServer: {host: 0.0.0.0,...},...}本地启动项目,获取 Network App running at:- Local: http://localhost:8080/ -…

JAVA 学习·泛型(二)——通配泛型

有关泛型的基本概念&#xff0c;参见我的前一篇博客 JAVA 学习泛型&#xff08;一&#xff09;。 协变性 泛型不具备协变性 在介绍通配泛型之前&#xff0c;先来看一下下面的例子。我们定义了一个泛型栈&#xff1a; import java.util.ArrayList; class GenericStack<E>…

全新TOF感知RGBD相机 | 高帧率+AI,探索3D感知新境界

海康机器人在近期的机器视觉新品发布会上推出的全新TOF感知RGBD相机,无疑是对当前机器视觉技术的一次革新。这款相机不仅融合了高帧率、轻松集成、体积小巧以及供电稳定等诸多优点,更重要的是,它将AI与3D感知技术完美结合,通过高帧率+AI算法,实现了对不同场景的快速捕捉与…

Android Studio报错:Constant expression required

【出现的问题】&#xff1a; 使用JDK17以上版本&#xff0c;switch语句报错&#xff1a;Constant expression required 【解决方法】&#xff1a; 在gradle.properties配置文件下添加代码&#xff1a; android.nonFinalResIdsfalse 如图&#xff1a; 接着再点击右上角的Sync…

asyncionetworkxFuncAnimation学习--动态显示计算图的运行情况

asyncio&networkx&FuncAnimation学习--动态显示计算图的运行情况 一.效果二.代码 一.目的 1.动态显示计算图的运行状态(点或边是否已完成) 二.步骤: 1.定义计算图 2.asyncio 并行计算 3.networkx 显示计算图 4.FuncAnimation 动态更新 三.依赖: conda install pygraphv…

Linux shell编程学习笔记48:touch命令

0 前言 touch是csdn技能树Linux基础练习题中最常见的一条命令&#xff0c;这次我们就来研究它的功能和用法。 1. touch命令的功能、格式和选项说明 我们可以使用命令 touch --help 来查看touch命令的帮助信息。 purpleEndurer bash ~ $ touch --help Usage: touch [OPTION]…

pyqt 按钮常用格式Qss设置

pyqt 按钮常用格式Qss设置 QSS介绍按钮常用的QSS设置效果代码 QSS介绍 Qt Style Sheets (QSS) 是 Qt 框架中用于定制应用程序界面样式的一种语言。它类似于网页开发中的 CSS&#xff08;Cascading Style Sheets&#xff09;&#xff0c;但专门为 Qt 应用程序设计。使用 QSS&am…

数据分析--客户价值分析RFM(分箱法/标准化)

原数据 原数据如果有异常或者缺失等情况&#xff0c;要先对数据进行处理 &#xff0c;再进行下面的操作&#xff0c;要不然会影响结果的正确性 一、根据RFM计算客户价值并对客户进行细分 1. 数据预处理 1.1 创建视图存储 R、F、M的最大最小值 创建视图存储R 、F、M 的最大最小…

力扣练习题(2024/5/2)

1填充每个节点的下一个右侧节点指针 给定一个 完美二叉树 &#xff0c;其所有叶子节点都在同一层&#xff0c;每个父节点都有两个子节点。二叉树定义如下&#xff1a; struct Node {int val;Node *left;Node *right;Node *next; } 填充它的每个 next 指针&#xff0c;让这个…
最新文章