行为型设计模式—状态模式

状态模式用于解决系统中复杂对象的状态转换以及不同状态下行为的封装问题。当系统中某个对象存在多个状态,这些状态之间可以进行转换,而且对象在不同状态下行为不相同时可以使用状态模式,把特定于状态的代码抽象到一组独立的状态类中避免过多的状态条件判断,减少维护成本。即使用Switch-case会有复杂if-else逻辑和大量代码冗余,把不同case抽离出不同的类即对应方法。

主要由环境类角色、抽象状态角色和具体状态角色,三个角色构成。

  • Context(环境类):环境类又称为上下文类,它定义客户端需要的接口,内部维护一个当前状态实例,并负责具体状态的切换。
  • State(抽象状态):定义状态下的行为,可以有一个或多个行为。
  • ConcreteState(具体状态):每一个具体状态类对应环境的一个具体状态,不同的具体状态类其行为有所不同。

下面用 Golang 实现状态模式来解构红绿灯在不同灯的状态下所具有的行为。

首先针对交通红绿灯,每种灯状态下都有亮灯、变灯、测速的行为,首先定义出交通灯的状态接口。

// State interface
type LightState interface {// 亮起当前状态的交通灯Light()// 转换到新状态的时候,调用的方法EnterState()// 设置一个状态要转变的状态NextLight(light *TrafficLight)// 检测车速CarPassingSpeed(*TrafficLight, int, string)
}

然后定义环境类 Context,它提供客户端调用状态行为的接口。

// Context
type TrafficLight struct {State LightStateSpeedLimit int
}func NewSimpleTrafficLight(speedLimit int) *TrafficLight {return &TrafficLight{SpeedLimit: speedLimit,State: NewRedState(),}
}

先定义一个DefaultLightState类型用于让具体的LightState 嵌套组合,减少公用法在每个具体 LightState 实现类中的重复实现。

type DefaultLightState struct {StateName string
}func (state *DefaultLightState) CarPassingSpeed(road *TrafficLight, speed int, licensePlate string) {if speed > road.SpeedLimit {fmt.Printf("Car with license %s was speeding\n", licensePlate)}
}func (state *DefaultLightState) EnterState(){fmt.Println("changed state to:", state.StateName)
}func (tl *TrafficLight) TransitionState(newState LightState) {tl.State = newStatetl.State.EnterState()
}

定义三个具体状态类型,去实现LightState接口,首先是红灯的状态实现。

接下来我们定义三个具体状态类型,去实现LightState接口,首先是红灯的状态实现。// 红灯状态
type redState struct {DefaultLightState
}func NewRedState() *redState {state := &redState{}state.StateName = "RED"return state
}func (state *redState) Light() {fmt.Println("红灯亮起,不可行驶")
}func (state *redState) CarPassingSpeed(light *TrafficLight, speed int, licensePlate string) {// 红灯时不能行驶, 所以这里要重写覆盖 DefaultLightState 里定义的这个方法if speed > 0 {fmt.Printf("Car with license \"%s\" ran a red light!\n", licensePlate)}
}func (state *redState) NextLight(light *TrafficLight){light.TransitionState(NewGreenState())
}

绿灯和黄灯状态

// 绿灯状态
type greenState struct{DefaultLightState
}func NewGreenState() *greenState{state :=  &greenState{}state.StateName = "GREEN"return state
}func (state *greenState) Light(){fmt.Println("绿灯亮起,请行驶")
}func (state *greenState) NextLight(light *TrafficLight){light.TransitionState(NewAmberState())
}// 黄灯状态
type amberState struct {DefaultLightState
}func NewAmberState() *amberState{state :=  &amberState{}state.StateName = "AMBER"return state
}func (state *amberState) Light(){fmt.Println("黄灯亮起,请注意")
}func (state *amberState) NextLight(light *TrafficLight){light.TransitionState(NewRedState())
}

状态实现类在内部确定了状态可以转换的下个状态,这样就把系统流程的状态机留在了内部,避免让客户端代码再去做状态链初始化和转换的判断,符合高内聚的设计原则,从而解放了客户端。

func main() {trafficLight := NewSimpleTrafficLight(500)interval := time.NewTicker(5 * time.Second)for {select {case <- interval.C:trafficLight.State.Light()trafficLight.State.CarPassingSpeed(trafficLight, 25, "CN1024")trafficLight.State.NextLight(trafficLight)default:}}
}

适用场景:

  • 如果对象需要根据自身当前状态进行不同行为, 同时状态的数量非常多且与状态相关的代码会频繁变更的话, 可使用状态模式。

    • 模式将所有特定于状态的代码抽取到一组独立的类中。 这样一来, 可以在独立于其他状态的情况下添加新状态或修改已有状态, 从而减少维护成本。
  • 如果某个类需要根据成员变量的当前值改变自身行为, 从而需要使用大量的条件语句时, 可使用该模式。

    • 状态模式会将这些条件语句的分支抽取到相应状态类的方法中。 通过业务逻辑内聚,减少客户端类的这部分工作。
  • 当相似状态和基于条件的状态机转换中存在许多重复代码时, 可使用状态模式。

    • 状态模式让你能够生成状态类层次结构, 通过将公用代码抽取到抽象基类中来减少重复。

缺点:

  • 状态模式的使用必然会增加系统中类和对象的个数,导致系统运行开销增大。
  • 状态模式的结构与实现都较为复杂,如果使用不当将导致程序结构和代码的混乱,增加系统设计的难度。
  • 状态模式对“开闭原则”的支持并不太好,增加新的状态类需要修改那些负责状态转换的源代码,否则无法转换到新增状态;而且修改某个状态类的行为也需修改对应类的源代码。

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

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

相关文章

操作系统详解(5)——信号(Signal)

系列文章&#xff1a; 操作系统详解(1)——操作系统的作用 操作系统详解(2)——异常处理(Exception) 操作系统详解(3)——进程、并发和并行 操作系统详解(4)——进程控制(fork, waitpid, sleep, execve) 文章目录 概述信号的种类Hardware EventsSoftware Events 信号的原理信号…

小程序开发公司哪家好?哪家最好?

小程序具有轻量、聚焦、快捷等特点&#xff0c;这有别于 web 端类和移动端 app 类产品。 小程序的第一印象非常关键&#xff0c;因此对于首页设计&#xff0c;关键要加强注意力表达&#xff0c;给予用户尽可能直观的信息感知&#xff0c;加快建立其对于业务价值的兴趣&#xf…

强化学习应用(八):基于Q-learning的无人机物流路径规划研究(提供Python代码)

一、Q-learning简介 Q-learning是一种强化学习算法&#xff0c;用于解决基于马尔可夫决策过程&#xff08;MDP&#xff09;的问题。它通过学习一个价值函数来指导智能体在环境中做出决策&#xff0c;以最大化累积奖励。 Q-learning算法的核心思想是通过不断更新一个称为Q值的…

Ubuntu 在线Swap扩容

1. 查看本机swap空间 free -h 2. 找一个较大的高速盘&#xff0c;创建swap的空间 mkdir /swap cd /swap sudo dd if/dev/zero ofswapfile bs50M count1k3.建swapfile&#xff0c;大小为bs*count 50M * 1k 50G 4.标记为Swap文件&#xff0c;让系统能识别交换文件。 sudo mk…

vue3hooks的使用

在 Vue 3 中&#xff0c;hooks 是用于封装组件逻辑的方法&#xff0c;类似于 Vue 2 中的 mixins。 使用 Hooks 可以提高代码的可维护性、可读性、可复用性和可测试性&#xff0c;降低代码之间的耦合度&#xff0c;使得组件的状态更加可控和可预测。 要使用 hooks&#xff0c;…

【JavaSE语法】图书管理系统实现详解

图片出处&#xff1a;The worlds biggest drone photo and video sharing platform | SkyPixel.com 导言 在学完JavaSE语法后&#xff0c;我们就可以去尝试写一个简单的图书管理系统来进一步提升我们面对对象编程的思想。在该系统中会涉及到数组&#xff0c;接口&#xff0c;封…

谷粒商城项目|es的应用场景及常见问题

es是什么 es多被用于搜索聚合分析引擎 是分布式的可以高性能查询的引擎 es应用场景 为什么不用MYSQL而用es es将数据存在内存中且可以分布式的存储数据 商品上架 商品在es中的保存 1.在es中建立索引 spu sku spu sku保存在一起防止分布查询 为了防止对象数组扁平化&#xff…

Unity摇杆+键鼠控制位移、旋转

1、位移 首先我们找到两张图片&#xff0c;一个大圆一个小圆&#xff0c;像这样&#xff1a; 结构是这样的&#xff1a; 然后&#xff0c;新建一个场景&#xff0c;用胶囊去做玩家&#xff0c;摄像机在胶囊下&#xff0c;并且在场景中放两个cube作为参照物 像这样搭好后&#…

探索商超货架场景目标检测性能,基于YOLOv8【n/s/m/l/x】全系列参数模型开发构建商超货架场景下亨氏米粉食品种类检测识别系统

在前面的系列博文中&#xff0c;我们陆续应用实践开发了很多有趣的项目&#xff0c;但是在密集排布场景下如商超购物场所内货架上货物种类目标检测模型的开发我们则少有涉及&#xff0c;正值周末&#xff0c;本文的主要目的就是想要实践构建这一场景下的目标检测模型&#xff0…

Seata 以 Nacos 为注册中心启动

Seata 以 Nacos 为注册中心启动 修改 conf 下的 application.yml 配置 server:port: 7091spring:application:name: seata-serverlogging:config: classpath:logback-spring.xmlfile:path: ${user.home}/logs/seataextend:logstash-appender:destination: 127.0.0.1:4560kafk…

FPGA之初探

FPGA的构成 基本逻辑单元CLB CLB是FPGA的基本逻辑单元&#xff0c; 一个 CLB 包括了 2 个 Slices&#xff0c;所以知道Slices的数量就可以知道FPGA的“大概”逻辑资源容量了。一个 Slice 等于 4 个6输入LUT8个触发器(flip-flop)算数运算逻辑&#xff0c;每个 Slice 的 4 个触发…

C Primer Plus(第六版)11.13 编程练习 第12题

/* 编写一个程序&#xff0c;读取输入&#xff0c;直至读到EOF,报告读入的单词数、大写字母数、小写字母数、标点 符号数和数字字符数。使用ctype.h头文件中的函数。 */ //测试字符串 //ajskm,dl kdAj,.lfj sjkdl sdk12lfj !.,fkdj.,.lssd.1a //(ajskm),(dl) (kdAj),.(lfj) (…

openssl3.2 - 官方demo学习 - cms - cms_denc.c

文章目录 openssl3.2 - 官方demo学习 - cms - cms_denc.c概述笔记END openssl3.2 - 官方demo学习 - cms - cms_denc.c 概述 将CMS数据结构写入PEM文件, 并将分离后的加密数据单独写到数据文件. 笔记 /*! \file cms_denc.c * \note openssl3.2 - 官方demo学习 - cms - cms_d…

AI与编程学习

在C语言中&#xff0c;指针通常与字符数组或字符串打交道时会涉及到ASCII码的转换&#xff0c;而不是用于表现多位数的第一位。48这个值对应的是ASCII码表中数字字符0的编码。 如果你有一个表示多位数的字符数组&#xff0c;例如&#xff1a; c char number[] "1234&qu…

【DC快速上手教程--1 Setup the DC】

DC快速上手教程--1 Setup the DC 0 Intro1 DC Demo 本篇系列教程介绍总结DC Flow&#xff0c;为了不涉密&#xff0c;在这里以DC labs为Demo做一个入门的介绍&#xff1b;目标&#xff1a;用起来EDA 工具是最基础也是最简单的&#xff1b;重点是如何去分析报告&#xff0c;依据…

vue3实现动态侧边菜单栏的几种方式总结

基于自建json数据的动态侧边菜单栏 后端接口json数据 src/api/menuList.js const menuList [{url: ,name: 人员管理,icon: icon-renyuan,menuId: 1,children: [{url: /user,name: 用户管理,icon: icon-jurassic_user,menuId: 1001,children: []},{url: /role,name: 角色管…

探索短链接:让网络分享更便捷

短链接是一种将长网址缩短为简洁形式的编码&#xff0c;它在互联网领域具有广泛的应用。本文将从多个方面介绍短链接的原理、类型、优势及应用场景&#xff0c;帮助您深入了解这一重要的网络技术。 短链接 | 一个覆盖广泛主题工具的高效在线平台(amd794.com) https://amd794.…

如何自动化部署和发布系统?

如何自动化部署和发布系统&#xff1f; 自动化部署和发布系统可以帮助开发人员更高效地部署和发布代码&#xff0c;减少手动操作的风险和错误。以下是一些自动化部署和发布系统的基本步骤&#xff1a; 选择合适的工具&#xff1a;选择适合你的项目和团队需求的自动化部署和发…

【LabVIEW FPGA 编程入门】使用FPGA IO进行编程

1.在项目中新建一个VI&#xff0c;命名为FPGA IO Test。 2. 可以直接将项目中的FPGA IO拖入程序框图中。 FPGA IO的类型&#xff1a; 数字线&#xff1a; 数字端口&#xff1a; 模拟IO&#xff1a; 其他&#xff1a; 3.如果新增加了FPGA资源&#xff0c;不是创建项目时扫描到的…

Linux centos stream9 parted

在Linux中&#xff0c;常用的磁盘管理工具包括 fdisk、parted、gdisk 等。它们可以用于创建、删除、调整分区、查看分区表等操作。 传统的MBR分区表(即主引导记录)大家都很熟悉&#xff0c;是过去我们使用windows时常见的。所支持的最大卷2T&#xff0c;且对分区有限制&#x…