状态模式
在软件开发过程中,我们经常会遇到这样的情况:一个对象的行为会随着其内部状态的改变而发生变化。例如,一个手机在不同状态下(开机、关机、静音等)对相同的操作(如来电)会有不同的反应。传统的解决方案可能会使用大量的条件判断语句(如 if - else 或 switch - case)来处理不同状态下的行为,但这种方式会使代码变得臃肿、难以维护和扩展。状态模式(State Pattern)应运而生,它提供了一种优雅的方式来处理对象状态变化及其对应的行为变化。
状态模式概述
状态模式是一种行为型设计模式,它允许对象在内部状态改变时改变其行为,就好像对象修改了它的类一样。状态模式主要包含以下几个角色:
- 环境类(Context):也称为上下文,它持有一个状态对象的引用,定义了客户端感兴趣的接口,并将与状态相关的操作委托给当前的状态对象来处理。
- 抽象状态类(State):定义了一个接口,用于封装与环境类的一个特定状态相关的行为。
- 具体状态类(ConcreteState):实现抽象状态类中定义的接口,每个具体状态类对应环境类的一个具体状态,实现该状态下的具体行为。
状态模式代码示例
以下是使用 Java 语言实现状态模式的示例代码。以一个简单的电梯控制系统为例,电梯有 “运行”、“停止” 两种状态,并且在不同状态下对 “开门”、“关门” 操作有不同的响应。
// 抽象状态类
abstract class LiftState {protected LiftContext liftContext;public void setLiftContext(LiftContext liftContext) {this.liftContext = liftContext;}public abstract void open();public abstract void close();public abstract void run();public abstract void stop();
}// 运行状态类
class RunningState extends LiftState {@Overridepublic void open() {System.out.println("电梯正在运行,无法开门");}@Overridepublic void close() {System.out.println("电梯正在运行,门已关闭");}@Overridepublic void run() {System.out.println("电梯正在运行");}@Overridepublic void stop() {System.out.println("电梯停止运行");liftContext.setState(liftContext.getStoppedState());}
}// 停止状态类
class StoppedState extends LiftState {@Overridepublic void open() {System.out.println("电梯停止,门打开");}@Overridepublic void close() {System.out.println("电梯停止,门关闭");liftContext.setState(liftContext.getRunningState());}@Overridepublic void run() {System.out.println("电梯开始运行");liftContext.setState(liftContext.getRunningState());}@Overridepublic void stop() {System.out.println("电梯已停止");}
}// 环境类
class LiftContext {private LiftState state;private RunningState runningState = new RunningState();private StoppedState stoppedState = new StoppedState();public LiftContext() {this.state = stoppedState;runningState.setLiftContext(this);stoppedState.setLiftContext(this);}public void setState(LiftState state) {this.state = state;}public RunningState getRunningState() {return runningState;}public StoppedState getStoppedState() {return stoppedState;}public void open() {state.open();}public void close() {state.close();}public void run() {state.run();}public void stop() {state.stop();}
}public class StatePatternDemo {public static void main(String[] args) {LiftContext liftContext = new LiftContext();liftContext.open();liftContext.close();liftContext.run();liftContext.stop();liftContext.open();}
}
在上述代码中,LiftState 是抽象状态类,定义了电梯在不同状态下可执行的操作接口。RunningState 和 StoppedState 是具体状态类,分别实现了电梯在 “运行” 和 “停止” 状态下的具体行为。LiftContext 是环境类,持有当前电梯的状态,并将对电梯的操作委托给当前状态对象。在 main 方法中,我们模拟了电梯的一系列操作,展示了状态模式下电梯在不同状态下的行为变化。
状态模式的应用场景
- 游戏开发:游戏角色在不同状态下(如站立、奔跑、跳跃、受伤等)对用户输入(如按键操作)会有不同的反应。通过状态模式,可以将不同状态下的行为封装在各自的具体状态类中,使代码结构更加清晰,易于维护和扩展。
- 工作流系统:在工作流系统中,任务可能处于不同的状态(如待处理、处理中、已完成等),并且在不同状态下对各种操作(如分配任务、提交任务等)有不同的处理方式。状态模式可以很好地处理这种情况,将工作流的状态管理和行为处理分离。
- 设备状态管理:对于各种设备(如打印机、空调等),它们在不同状态下(如开启、关闭、故障等)对用户操作(如打印指令、温度调节等)的响应不同。使用状态模式可以方便地实现设备状态的管理和相应行为的处理。
状态模式的优缺点
- 优点
- 清晰的状态管理:状态模式将对象的状态和行为封装在不同的具体状态类中,使得代码结构更加清晰,易于理解和维护。每个状态类专注于实现该状态下的特定行为,符合单一职责原则。
- 易于扩展:当需要添加新的状态或修改现有状态的行为时,只需要创建新的具体状态类或修改现有状态类的代码,而不需要在大量的条件判断语句中进行修改,符合开闭原则。
- 提高可维护性:避免了使用大量的条件判断语句,使得代码更加简洁,减少了出错的可能性。同时,当状态变化时,只需要修改相应状态类的代码,而不会影响其他状态的行为。
- 缺点
- 增加类的数量:由于每个状态都需要一个具体的状态类来实现,可能会导致类的数量增加,使项目的代码结构变得复杂。在小型项目中,这种复杂性可能会显得过于冗余。
- 状态转换逻辑复杂:在一些复杂的应用场景中,状态之间的转换逻辑可能会变得复杂,需要仔细设计和维护状态转换的条件和顺序,否则可能会导致状态转换错误或不符合预期的行为。
结语
希望本文能帮助您更好地理解状态模式的概念及其实际应用。如果您有任何疑问或建议,请随时留言交流。
相关文章:
状态模式
在软件开发过程中,我们经常会遇到这样的情况:一个对象的行为会随着其内部状态的改变而发生变化。例如,一个手机在不同状态下(开机、关机、静音等)对相同的操作(如来电)会有不同的反应。传统的解…...
RoHS 简介
RoHS(Restriction of Hazardous Substances Directive,限制有害物质指令)是欧盟制定的一项环保法规,旨在限制电气和电子设备中某些有害物质的使用,以减少这些产品对环境和人体健康的危害。 RoHS限制的有害物质及其限量…...
【Vim Masterclass 笔记26】S11L46:Vim 插件的安装、使用与日常管理
文章目录 Section 11:Vim PluginsS11L46 Managing Vim Plugins1 第三方插件管理工具2 安装插件使用的搜索引擎3 Vim 插件的安装方法4 存放 Vim 插件包的路径格式5 示例一:插件 NERDTree 的安装6 示例二:插件 ctrlp.vim 的安装7 示例三&#x…...
深度学习原理与Pytorch实战
深度学习原理与Pytorch实战 第2版 强化学习人工智能神经网络书籍 python动手学深度学习框架书 TransformerBERT图神经网络: 技术讲解 编辑推荐 1.基于PyTorch新版本,涵盖深度学习基础知识和前沿技术,由浅入深,通俗易懂…...
ELK环境搭建
文章目录 1.ElasticSearch安装1.安装的版本选择1.SpringBoot版本:2.4.2 找到依赖的spring-data-elasticsearch的版本2.spring-data-elasticsearch版本:4.1.3 找到依赖的elasticsearch版本3.elasticsearch版本:7.9.3 2.安装1.官方文档2.下载压…...
基于Springboot + vue实现的民俗网
“前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站:人工智能学习网站” 💖学习知识需费心, 📕整理归纳更费神。 🎉源码免费人人喜…...
第24篇 基于ARM A9处理器用汇编语言实现中断<六>
Q:怎样设计ARM处理器汇编语言程序使用定时器中断实现实时时钟? A:此前我们曾使用轮询定时器I/O的方式实现实时时钟,而在本实验中将采用定时器中断的方式。新增第三个中断源A9 Private Timer,对该定时器进行配置&#…...
【数据结构】_不带头非循环单向链表
目录 1. 链表的概念及结构 2. 链表的分类 3. 单链表的实现 3.1 SList.h头文件 3.2 SList.c源文件 3.3 Test_SList.c测试文件 关于线性表,已介绍顺序表,详见下文: 【数据结构】_顺序表-CSDN博客 本文介绍链表; 基于顺序表…...
golang 使用双向链表作为container/heap的载体
MyHeap:container/heap的数据载体,需要实现以下方法: Len:堆中数据个数 Less:第i个元素 是否必 第j个元素 值小 Swap:交换第i个元素和 第j个元素 Push:向堆中追加元素 Pop:从堆…...
C#集合操作优化:高效实现批量添加与删除
在C#中,对集合进行批量操作(如批量添加或删除元素)通常涉及使用集合类型提供的方法和特性,以及可能的循环或LINQ查询来高效地处理大量数据。以下是一些常见的方法和技巧: 批量添加元素 使用集合的AddRange方法&#x…...
142.WEB渗透测试-信息收集-小程序、app(13)
免责声明:内容仅供学习参考,请合法利用知识,禁止进行违法犯罪活动! 内容参考于: 易锦网校会员专享课 上一个内容:141.WEB渗透测试-信息收集-小程序、app(12) 软件用法,…...
24.日常算法
1. 数组中两元素的最大乘积 题目来源 给你一个整数数组 nums,请你选择数组的两个不同下标 i 和 j,使 (nums[i]-1)*(nums[j]-1) 取得最大值。请你计算并返回该式的最大值。 示例 1: 输入:nums [3,4,5,2] 输出:12 解释…...
分布式理解
分布式 如何理解分布式 狭义的分布是指,指多台PC在地理位置上分布在不同的地方。 分布式系统 分布式系**统:**多个能独立运行的计算机(称为结点)组成。各个结点利用计算机网络进行信息传递,从而实现共同的“目标或者任…...
wordpress调用指定ID页面的链接
在WordPress中,如果你想调用一个指定ID的页面链接,可以使用以下几种方法: 方法一:使用页面ID 你可以直接使用页面的ID来生成链接。例如,如果你想链接到ID为123的页面,可以使用以下代码: <…...
单值二叉树(C语言详解版)
一、摘要 今天要讲的是leetcode单值二叉树,这里用到的C语言,主要提供的是思路,大家看了我的思路之后可以点击链接自己试一下。 二、题目简介 如果二叉树每个节点都具有相同的值,那么该二叉树就是单值二叉树。 只有给定的树是单…...
python学opencv|读取图像(四十二)使用cv2.add()函数实现多图像叠加
【1】引言 前序学习过程中,掌握了灰度图像和彩色图像的掩模操作: python学opencv|读取图像(九)用numpy创建黑白相间灰度图_numpy生成全黑图片-CSDN博客 python学opencv|读取图像(四十)掩模:三…...
速通Docker === Docker Compose
目录 Docker Compose 简介 Docker Compose 常用命令 使用 Docker Compose 启动 WordPress 普通启动方式(使用 Docker 命令) 使用 Docker Compose 启动 Docker Compose 的特性 Docker Compose 简介 Docker Compose 是一个用于定义和运行多容器 Dock…...
LMI Gocator GO_SDK VS2019引用配置
LMI SDK在VS2019中的引用是真的坑爹,总结一下经验,希望后来的人能少走弯路.大致内容如下: (1) 环境变量 (2)C/C 附加包含目录 E:\GWQ\Gocator\GO_SDK\Gocator\GoSdk E:\GWQ\Gocator\GO_SDK\Platform\kApi (3&#…...
技术之翼,创作之心
引言:初入编程的迷茫与追求 当我第一次接触到编程时,心中充满了既期待又迷茫的情感。那时,我还是一名刚刚踏入大学的学生,面对一门陌生而复杂的学科——计算机科学,我的内心充满了好奇与困惑。课堂上,老师…...
WebSocket异步导出
WebSocket异步导出 1、安装sockjs-client和stompjs2、连接后台3、vite.config.ts 配置反向代理4、导出并实时通信5、 封装WebSocket 文件注册登录(城通网盘) 1、安装sockjs-client和stompjs import SockJS from sockjs-client/dist/sockjs.min.js import Stomp from stompjs2、…...
Cursor实现用excel数据填充word模版的方法
cursor主页:https://www.cursor.com/ 任务目标:把excel格式的数据里的单元格,按照某一个固定模版填充到word中 文章目录 注意事项逐步生成程序1. 确定格式2. 调试程序 注意事项 直接给一个excel文件和最终呈现的word文件的示例,…...
在 Nginx Stream 层“改写”MQTT ngx_stream_mqtt_filter_module
1、为什么要修改 CONNECT 报文? 多租户隔离:自动为接入设备追加租户前缀,后端按 ClientID 拆分队列。零代码鉴权:将入站用户名替换为 OAuth Access-Token,后端 Broker 统一校验。灰度发布:根据 IP/地理位写…...
Springcloud:Eureka 高可用集群搭建实战(服务注册与发现的底层原理与避坑指南)
引言:为什么 Eureka 依然是存量系统的核心? 尽管 Nacos 等新注册中心崛起,但金融、电力等保守行业仍有大量系统运行在 Eureka 上。理解其高可用设计与自我保护机制,是保障分布式系统稳定的必修课。本文将手把手带你搭建生产级 Eur…...
浅谈不同二分算法的查找情况
二分算法原理比较简单,但是实际的算法模板却有很多,这一切都源于二分查找问题中的复杂情况和二分算法的边界处理,以下是博主对一些二分算法查找的情况分析。 需要说明的是,以下二分算法都是基于有序序列为升序有序的情况…...
【Oracle】分区表
个人主页:Guiat 归属专栏:Oracle 文章目录 1. 分区表基础概述1.1 分区表的概念与优势1.2 分区类型概览1.3 分区表的工作原理 2. 范围分区 (RANGE Partitioning)2.1 基础范围分区2.1.1 按日期范围分区2.1.2 按数值范围分区 2.2 间隔分区 (INTERVAL Partit…...
ip子接口配置及删除
配置永久生效的子接口,2个IP 都可以登录你这一台服务器。重启不失效。 永久的 [应用] vi /etc/sysconfig/network-scripts/ifcfg-eth0修改文件内内容 TYPE"Ethernet" BOOTPROTO"none" NAME"eth0" DEVICE"eth0" ONBOOT&q…...
sipsak:SIP瑞士军刀!全参数详细教程!Kali Linux教程!
简介 sipsak 是一个面向会话初始协议 (SIP) 应用程序开发人员和管理员的小型命令行工具。它可以用于对 SIP 应用程序和设备进行一些简单的测试。 sipsak 是一款 SIP 压力和诊断实用程序。它通过 sip-uri 向服务器发送 SIP 请求,并检查收到的响应。它以以下模式之一…...
R 语言科研绘图第 55 期 --- 网络图-聚类
在发表科研论文的过程中,科研绘图是必不可少的,一张好看的图形会是文章很大的加分项。 为了便于使用,本系列文章介绍的所有绘图都已收录到了 sciRplot 项目中,获取方式: R 语言科研绘图模板 --- sciRplothttps://mp.…...
macOS 终端智能代理检测
🧠 终端智能代理检测:自动判断是否需要设置代理访问 GitHub 在开发中,使用 GitHub 是非常常见的需求。但有时候我们会发现某些命令失败、插件无法更新,例如: fatal: unable to access https://github.com/ohmyzsh/oh…...
Tauri2学习笔记
教程地址:https://www.bilibili.com/video/BV1Ca411N7mF?spm_id_from333.788.player.switch&vd_source707ec8983cc32e6e065d5496a7f79ee6 官方指引:https://tauri.app/zh-cn/start/ 目前Tauri2的教程视频不多,我按照Tauri1的教程来学习&…...
