【设计模式】【行为型模式】状态模式(State)
👋hi,我不是一名外包公司的员工,也不会偷吃茶水间的零食,我的梦想是能写高端CRUD
🔥 2025本人正在沉淀中… 博客更新速度++
📫 欢迎+V: flzjcsg2,我们共同讨论Java深渊的奥秘
🎵 当你的天空突然下了大雨,那是我在为你炸乌云
文章目录
- 一、入门
- 什么是状态模式?
- 为什么需要状态模式?
- 怎样实现状态模式?
- 二、状态模式在源码中的运用
- Java线程管理
- Spring 状态机(Spring State Machine)
- 三、总结
- 状态模式的优点
- 状态模式的缺点
- 状态模式的使用场景
- 参考
一、入门
什么是状态模式?
状态模式(State Pattern)是一种行为设计模式,允许对象在其内部状态改变时改变其行为,使其看起来像是改变了类。状态模式的核心思想是将对象的状态封装成独立的类,并将行为委托给代表当前状态的对象。
为什么需要状态模式?
有这样一个场景,如果不用状态模式,那么电题的每一个动作(open、close、stop、run),都会先对状态进行判断,再决定能否执行(比如运行中的电梯是不能开门的),类图如下所示。
public class Lift implements ILift {private int state;@Overridepublic void setState(int state) {this.state = state;}//执行关门动作@Overridepublic void close() {switch (this.state) {case OPENING_STATE:System.out.println("电梯关门了。。。");//只有开门状态可以关闭电梯门,可以对应电梯状态表来看this.setState(CLOSING_STATE);//关门之后电梯就是关闭状态了break;case CLOSING_STATE://do nothing //已经是关门状态,不能关门break;case RUNNING_STATE://do nothing //运行时电梯门是关着的,不能关门break;case STOPPING_STATE://do nothing //停止时电梯也是关着的,不能关门break;}}//执行开门动作@Overridepublic void open() {switch (this.state) {case OPENING_STATE://门已经开了,不能再开门了//do nothingbreak;case CLOSING_STATE://关门状态,门打开:System.out.println("电梯门打开了。。。");this.setState(OPENING_STATE);break;case RUNNING_STATE://do nothing 运行时电梯不能开门break;case STOPPING_STATE:System.out.println("电梯门开了。。。");//电梯停了,可以开门了this.setState(OPENING_STATE);break;}}//执行运行动作@Overridepublic void run() {switch (this.state) {case OPENING_STATE://电梯不能开着门就走//do nothingbreak;case CLOSING_STATE://门关了,可以运行了System.out.println("电梯开始运行了。。。");this.setState(RUNNING_STATE);//现在是运行状态break;case RUNNING_STATE://do nothing 已经是运行状态了break;case STOPPING_STATE:System.out.println("电梯开始运行了。。。");this.setState(RUNNING_STATE);break;}}//执行停止动作@Overridepublic void stop() {switch (this.state) {case OPENING_STATE: //开门的电梯已经是是停止的了(正常情况下)//do nothingbreak;case CLOSING_STATE://关门时才可以停止System.out.println("电梯停止了。。。");this.setState(STOPPING_STATE);break;case RUNNING_STATE://运行时当然可以停止了System.out.println("电梯停止了。。。");this.setState(STOPPING_STATE);break;case STOPPING_STATE://do nothingbreak;}}
}
在没有使用状态模式的情况下,通常会使用大量的if-else
或switch-case
语句来处理对象在不同状态下的行为。这种方式会带来以下问题:
- 代码臃肿且难以维护:当状态数量增加时,
if-else
或switch-case
语句会变得非常冗长。每次新增状态或修改状态转换逻辑时,都需要修改这些条件语句,容易引入错误。 - 违反开闭原则:开闭原则要求软件实体(类、模块、函数等)对扩展开放,对修改关闭。使用条件语句时,新增状态或修改行为需要修改现有代码,而不是扩展。
- 状态和行为耦合:状态和行为逻辑通常混杂在一起,导致代码难以理解和测试。如果需要复用某些状态的行为,很难将其提取出来。
- 难以扩展:当状态转换逻辑变得复杂时(例如,某些状态的下一个状态有多个分支),条件语句会变得更加混乱。新增状态或修改状态转换逻辑时,可能会影响其他状态的逻辑。
怎样实现状态模式?
状态模式的组成:
- Context(上下文):它定义了客户程序需要的接口,维护一个当前状态,并将与状态相关的操作委托给当前状态对象来处理。
- State(状态):定义一个接口,封装与Context的特定状态相关的行为。
- ConcreteState(具体状态):实现State接口,定义与Context的某个状态相关的行为。
【案例】 电梯状态改进版
State(状态):LiftState
类
//抽象状态类
public abstract class LiftState {//定义一个环境角色,也就是封装状态的变化引起的功能变化protected Context context;public void setContext(Context context) {this.context = context;}//电梯开门动作public abstract void open();//电梯关门动作public abstract void close();//电梯运行动作public abstract void run();//电梯停止动作public abstract void stop();
}
ConcreteState(具体状态):OpenningState
类、RunningState
类、StoppingState
类和ClosingState
类
//开启状态
public class OpenningState extends LiftState {//开启当然可以关闭了,我就想测试一下电梯门开关功能@Overridepublic void open() {System.out.println("电梯门开启...");}@Overridepublic void close() {//状态修改super.context.setLiftState(Context.closeingState);//动作委托为CloseState来执行,也就是委托给了ClosingState子类执行这个动作super.context.getLiftState().close();}//电梯门不能开着就跑,这里什么也不做@Overridepublic void run() {//do nothing}//开门状态已经是停止的了@Overridepublic void stop() {//do nothing}
}//运行状态
public class RunningState extends LiftState {//运行的时候开电梯门?你疯了!电梯不会给你开的@Overridepublic void open() {//do nothing}//电梯门关闭?这是肯定了@Overridepublic void close() {//虽然可以关门,但这个动作不归我执行//do nothing}//这是在运行状态下要实现的方法@Overridepublic void run() {System.out.println("电梯正在运行...");}//这个事绝对是合理的,光运行不停止还有谁敢做这个电梯?!估计只有上帝了@Overridepublic void stop() {super.context.setLiftState(Context.stoppingState);super.context.stop();}
}//停止状态
public class StoppingState extends LiftState {//停止状态,开门,那是要的!@Overridepublic void open() {//状态修改super.context.setLiftState(Context.openningState);//动作委托为CloseState来执行,也就是委托给了ClosingState子类执行这个动作super.context.getLiftState().open();}@Overridepublic void close() {//虽然可以关门,但这个动作不归我执行//状态修改super.context.setLiftState(Context.closeingState);//动作委托为CloseState来执行,也就是委托给了ClosingState子类执行这个动作super.context.getLiftState().close();}//停止状态再跑起来,正常的很@Overridepublic void run() {//状态修改super.context.setLiftState(Context.runningState);//动作委托为CloseState来执行,也就是委托给了ClosingState子类执行这个动作super.context.getLiftState().run();}//停止状态是怎么发生的呢?当然是停止方法执行了@Overridepublic void stop() {System.out.println("电梯停止了...");}
}//关闭状态
public class ClosingState extends LiftState {@Override//电梯门关闭,这是关闭状态要实现的动作public void close() {System.out.println("电梯门关闭...");}//电梯门关了再打开,逗你玩呢,那这个允许呀@Overridepublic void open() {super.context.setLiftState(Context.openningState);super.context.open();}//电梯门关了就跑,这是再正常不过了@Overridepublic void run() {super.context.setLiftState(Context.runningState);super.context.run();}//电梯门关着,我就不按楼层@Overridepublic void stop() {super.context.setLiftState(Context.stoppingState);super.context.stop();}
}
Context(上下文):Context
类
//环境角色
public class Context {//定义出所有的电梯状态public final static OpenningState openningState = new OpenningState();//开门状态,这时候电梯只能关闭public final static ClosingState closeingState = new ClosingState();//关闭状态,这时候电梯可以运行、停止和开门public final static RunningState runningState = new RunningState();//运行状态,这时候电梯只能停止public final static StoppingState stoppingState = new StoppingState();//停止状态,这时候电梯可以开门、运行//定义一个当前电梯状态private LiftState liftState;public LiftState getLiftState() {return this.liftState;}public void setLiftState(LiftState liftState) {//当前环境改变this.liftState = liftState;//把当前的环境通知到各个实现类中this.liftState.setContext(this);}public void open() {this.liftState.open();}public void close() {this.liftState.close();}public void run() {this.liftState.run();}public void stop() {this.liftState.stop();}
}
测试类
//测试类
public class Client {public static void main(String[] args) {Context context = new Context();context.setLiftState(new ClosingState());context.open();context.close();context.run();context.stop();}
}
二、状态模式在源码中的运用
Java线程管理
Java 的线程(Thread)生命周期就是一个典型的状态模式应用。线程的状态包括NEW
、RUNNABLE
、BLOCKED
、WAITING
、TIMED_WAITING
和 TERMINATED
。虽然 Java 线程的状态管理没有直接使用状态模式的设计,但其设计思想与状态模式非常相似。
线程状态转换:
NEW
->RUNNABLE
(调用start()
方法)RUNNABLE
->BLOCKED
(等待锁)RUNNABLE
->WAITING
(调用wait()
方法)RUNNABLE
->TIMED_WAITING
(调用sleep()
或join()
方法)RUNNABLE
->TERMINATED
(线程执行完毕)
如果使用状态模式来实现线程状态管理,可以这样设计:
interface ThreadState {void start(ThreadContext context);void run(ThreadContext context);void block(ThreadContext context);void terminate(ThreadContext context);
}class NewState implements ThreadState {@Overridepublic void start(ThreadContext context) {context.setState(new RunnableState());}@Overridepublic void run(ThreadContext context) {throw new IllegalStateException("线程未启动,无法运行。");}@Overridepublic void block(ThreadContext context) {throw new IllegalStateException("线程未启动,无法阻塞。");}@Overridepublic void terminate(ThreadContext context) {throw new IllegalStateException("线程未启动,无法终止。");}
}class RunnableState implements ThreadState {@Overridepublic void start(ThreadContext context) {throw new IllegalStateException("线程已启动,无需再次启动。");}@Overridepublic void run(ThreadContext context) {System.out.println("线程正在运行...");}@Overridepublic void block(ThreadContext context) {context.setState(new BlockedState());}@Overridepublic void terminate(ThreadContext context) {context.setState(new TerminatedState());}
}class ThreadContext {private ThreadState state;public ThreadContext() {this.state = new NewState();}public void setState(ThreadState state) {this.state = state;}public void start() {state.start(this);}public void run() {state.run(this);}public void block() {state.block(this);}public void terminate() {state.terminate(this);}
}
Spring 状态机(Spring State Machine)
Spring 提供了一个状态机框架(Spring State Machine),用于管理复杂的状态转换逻辑。它基于状态模式设计,支持状态、事件、转换等概念。
@Configuration
@EnableStateMachine
public class StateMachineConfig extends StateMachineConfigurerAdapter<String, String> {@Overridepublic void configure(StateMachineStateConfigurer<String, String> states) throws Exception {states.withStates().initial("SI") // 初始状态.state("S1") // 状态 S1.state("S2"); // 状态 S2}@Overridepublic void configure(StateMachineTransitionConfigurer<String, String> transitions) throws Exception {transitions.withExternal().source("SI").target("S1").event("E1") // 事件 E1 触发 SI -> S1.and().withExternal().source("S1").target("S2").event("E2"); // 事件 E2 触发 S1 -> S2}
}
在这个例子中,Spring State Machine 使用状态模式来管理状态和事件驱动的转换。
三、总结
状态模式的优点
- 消除复杂的条件语句:状态模式将状态相关的行为分散到各个状态类中,避免了大量的
if-else
或switch-case
语句,使代码更加清晰。 - 符合开闭原则:新增状态时,只需添加新的状态类,而无需修改现有代码。修改某个状态的行为时,只需修改对应的状态类,不会影响其他状态。
- 提高代码的可读性和可维护性:状态和行为被封装在独立的类中,代码结构更加清晰。每个状态类的职责单一,易于理解和测试。
- 简化上下文逻辑:上下文(Context)只需维护当前状态的引用,并将行为委托给状态对象,逻辑更加简洁。
- 支持复杂的状态转换:状态模式可以轻松处理多分支的状态转换逻辑(例如,一个状态的下一个状态有多个可能)。
状态模式的缺点
- 增加类的数量:每个状态都需要一个单独的类,可能会导致类的数量增加,代码结构变得复杂。
- 状态转换逻辑分散:状态转换逻辑分散在各个状态类中,可能会导致状态转换规则不够直观。
- 不适合简单的状态管理:如果状态数量很少且状态转换逻辑简单,使用状态模式可能会显得过度设计。
状态模式的使用场景
- 对象的行为依赖于其状态,且状态数量较多:例如,订单系统(新建、已支付、已发货、已完成等状态)。
- 状态转换逻辑复杂:例如,游戏角色的状态(站立、跑步、跳跃、攻击等),每个状态的行为和转换条件不同。
- 需要避免大量的条件语句:例如,线程状态管理(新建、运行、阻塞、终止等),使用状态模式可以避免复杂的条件判断。
- 状态和行为需要动态切换:例如,TCP 连接状态(建立连接、数据传输、关闭连接等),状态模式可以灵活地管理状态转换。
- 需要支持分层状态或子状态:例如,工作流引擎中的状态(主状态和子状态),状态模式可以很好地支持分层状态管理。
参考
黑马程序员Java设计模式详解, 23种Java设计模式(图解+框架源码分析+实战)_哔哩哔哩_bilibili
相关文章:

【设计模式】【行为型模式】状态模式(State)
👋hi,我不是一名外包公司的员工,也不会偷吃茶水间的零食,我的梦想是能写高端CRUD 🔥 2025本人正在沉淀中… 博客更新速度 📫 欢迎V: flzjcsg2,我们共同讨论Java深渊的奥秘 …...
PostgreSQL错误: 编码“UTF8“的字符0x0xe9 0x94 0x99在编码“WIN1252“没有相对应值
错误介绍 今天遇到一个错误,记录一下 2025-02-10 17:04:35.264 HKT [28816] 错误: 编码"WIN1252"的字符0x0x81在编码"UTF8"没有相对应值 2025-02-10 17:04:35.264 HKT [28816] 错误: 编码"UTF8"的字符0x0xe9 0x94 0x99在编码&quo…...

Mac ARM 架构的命令行(终端)中,删除整行的快捷键是:Ctrl + U
在 Mac ARM 架构的命令行(终端)中,删除整行的快捷键是: Ctrl U这个快捷键会删除光标所在位置到行首之间的所有内容。如果你想删除光标后面的所有内容,可以使用: Ctrl K这两个快捷键可以帮助你快速清除当…...
Vue2下判断有新消息来时以站内信方式在页面右下角弹出
以下是完整的Vue2全局通知组件实现方案,包含自动挂载和全局调用方法: 第一步:创建通知组件 <!-- src/components/Notification/index.vue --> <template><div class"notification-container"><transition-g…...

AI语言模型的技术之争:DeepSeek与ChatGPT的架构与训练揭秘
云边有个稻草人-CSDN博客 目录 第一章:DeepSeek与ChatGPT的基础概述 1.1 DeepSeek简介 1.2 ChatGPT简介 第二章:模型架构对比 2.1 Transformer架构:核心相似性 2.2 模型规模与参数 第三章:训练方法与技术 3.1 预训练与微调…...

网络安全中的account和audit区别
一、AWD介绍 AWD:Attack With Defence,即攻防对抗,比赛中每个队伍维护多台服务器(一般两三台,视小组参赛人数而定),服务器中存在多个漏洞(web层、系统层、中间件层等)&a…...

Visual Studio 使用 “Ctrl + /”键设置注释和取消注释
问题:在默认的Visual Studio中,选择单行代码后,按下Ctrl /键会将代码注释掉,但再次按下Ctrl /键时,会进行双重注释,这不是我们想要的。 实现效果:当按下Ctrl /键会将代码注释掉,…...
【密评】 | 商用密码应用安全性评估从业人员考核题库(23)
在GM/T0048《智能密码钥匙密码检测规范》中,产品的对称算法性能应满足哪个标准中的要求()。 A.GM/T 0016《智能密码钥匙密码应用接口规范》 B.GM/T 0017《智能密码钥匙密码应用接口数据格式规范》 C.GM/T 0027《智能密码钥匙技术规范》 D.GM/T 0028《密码模块安全技术要求》…...

【MySQL】幻读 案例分析
目录 假设1:只在 id5 这一行加锁,其他行不加锁? 幻读的定义 幻读的场景 假设1 产生的问题:语义被破坏 假设1 产生的问题:数据一致性 结论: 假设1不成立 假设2:扫描过程中每一行都加上写锁…...
10bit VS 8bit 视频:色彩深度的较量,谁才是视觉盛宴的王者?
10bit 和 8bit 视频 10bit 视频和 8bit 视频的主要区别在于色彩深度和细节表现能力。10bit 视频具有更高的色彩深度和更丰富的细节表现,能够提供更平滑的色彩过渡和更真实的图像质量,但需要更多的存储空间和带宽。8bit 视频则在存储和传输方面更加高效,适合于对存储空间和带…...
讲解下MySql的外连接查询在SpringBoot中的使用情况
在Spring Boot中使用MySQL的外连接查询时,通常通过JPA、MyBatis或JDBC等持久层框架来实现。外连接查询主要用于从多个表中获取数据,即使某些表中没有匹配的记录。外连接分为左外连接(LEFT JOIN)、右外连接(RIGHT JOIN&…...
蓝桥杯试题:归并排序
一、问题描述 在一个神秘的岛屿上,有一支探险队发现了一批宝藏,这批宝藏是以整数数组的形式存在的。每个宝藏上都标有一个数字,代表了其珍贵程度。然而,由于某种神奇的力量,这批宝藏的顺序被打乱了,探险队…...
物联网(IoT)如何与人工智能(AI)的结合
物联网(IoT)与人工智能(AI)的结合是当前技术发展的重要趋势,通常被称为 AIoT(人工智能物联网)。这种结合通过将AI的计算能力和数据分析能力与物联网的海量设备连接能力相结合,实现了…...

一致性Hash算法延伸至Redis分片扩容使Lua脚本失效如何解决
文章部分内容来源:小林coding 问题场景:我们需要用Lua脚本,并且这个Lua脚本需要用到两个Key,但这两个Key必须命中同一台机器才可以,不然Lua脚本就会执行失败。如果集群扩容可能会导致两个Key落到不同的节点上导致Lua脚…...
Idea 插件 Quickly-Code-Toolkit
使用说明 (一)全局设置 Paging Wrapper Setting(分页设置) 功能:主要用于在方法写入时,为返回参数提供分页包装类。设置方式:需准确填写分页包装类的全限定名,例如:com…...
先进制造aps专题二十九 基于ai智能体的生产排程和工厂生产仿真引擎的设计
上文中,我们说,通常的做法是,可以先通过排产仿真引擎产生生产计划,再在工厂仿真引擎里仿真执行,这样可以预先分析计划和执行的差异情况并进行调整优化 这里的产生生产计划,仿真生产执行和数据分析都是人工…...

【Cocos TypeScript 零基础 15.1】
目录 见缝插针UI脚本针脚本球脚本心得_旋转心得_更改父节点心得_缓动动画成品展示图 见缝插针 本人只是看了老师的大纲,中途不明白不会的时候再去看的视频 所以代码可能与老师代码有出入 SIKI_学院_点击跳转 UI脚本 import { _decorator, Camera, color, Component, directo…...

利用邮件合并将Excel的信息转为Word(单个测试用例转Word)
利用邮件合并将Excel的信息转为Word 效果一览效果前效果后 场景及问题解决方案 一、准备工作准备Excel数据源准备Word模板 二、邮件合并操作步骤连接Excel数据源插入合并域预览并生成合并文档 效果一览 效果前 效果后 场景及问题 在执行项目时的验收阶段,对于测试…...

尚硅谷课程【笔记】——大数据之Hadoop【一】
课程视频链接:尚硅谷Hadoop2.x框架入门 一、大数据概论 1)大数据概念 大数据(Big Data):指无法再一定时间范围内用常规软件工具进行捕捉、管理和处理的数据集合,是需要新处理模式才能具有更强的决策力、洞…...
C++ ——基础进阶
1、引用 概念:相当于给变量取个别名,通过使用&在变量定义时定义 1.1 性质 (1)成为一个变量的引用后,就不能成为其他变量的引用 int a1; int& a_citea; int b90; a_citeb; //相当于把b的值给了a_cite cout&l…...

基于STM32语音识别柔光台灯
基于STM32语音识别柔光台灯 (程序+原理图+PCB+设计报告) 功能介绍 具体功能: 基于语音识别的智能LED柔光台灯设计,主要包括语音识别模块应用,PWM波控制LED柔光灯的亮度,…...
数据结构之LinkedList
系列文章目录 数据结构之ArrayList-CSDN博客 目录 系列文章目录 前言 一、模拟实现链表 1. 遍历链表 2. 插入节点 3. 删除节点 4. 清空链表 二、链表的常见操作 1. 反转链表 2. 返回链表的中间节点 3. 链表倒数第 k 个节点 4. 合并两个有序链表 5. 分割链表 6. 判…...
Python使用clickhouse-local和MySQL表函数实现从MySQL到ClickHouse数据同步
下面是一个使用clickhouse-local和MySQL表函数实现从MySQL到ClickHouse数据同步的Python解决方案,包含全量同步、增量同步和测试用例。 此解决方案提供了生产级数据同步所需的核心功能,可根据具体场景扩展更多高级特性如:数据转换、字段映射…...

实践指南:从零开始搭建RAG驱动的智能问答系统
LLM 赋能的最强大的应用之一是复杂的问答 (Q&A) 聊天机器人。这些是可以回答关于特定来源信息问题的应用程序。这些应用程序使用一种称为检索增强生成的技术,或 RAG。本文将展示如何基于 LangChain 构建一个简单的基于非结构化数据文本数据源的问答应用程序。 温…...

Python 3.11.9 安装教程
前言 记录一下Windows环境下Python解释器的安装过程。 安装过程 1、安装程序下载 打开Python官网: 点击Downloads,选择Windows: 页面中找到需要的3.11.9版本,点击Download Windows installer (64-bit)下载: 2、…...

JMM初学
文章目录 1,线程间的同步和通信1.1, 共享内存并发模型 (Shared Memory Model)线程通信机制线程同步机制特点 1.2, 消息传递并发模型 (Message Passing Model)线程通信机制线程同步机制特点 适用场景对比 2,Java内存模型JMM2.0,Java内存模型的基础(1)内存…...
Flink checkpoint
对齐检查点 (Aligned Checkpoint) Flink 的分布式快照机制受到 Chandy-Lamport 算法的启发。 其核心元素是数据流中的屏障(Barrier)。 Barrier 注入 :JobManager 中的 Checkpoint Coordinator 指示 Source 任务开始 Checkpoint。Source 任务…...

从 LeetCode 到日志匹配:一行 Swift 实现规则识别
文章目录 摘要描述题解答案题解代码分析示例测试及结果时间复杂度空间复杂度总结 摘要 在开发中我们经常遇到“模式匹配”的问题,比如日志分类、用户意图识别、甚至是在一些权限系统中做规则映射判断。这类问题的本质是判断两个结构是否具有一致的对应关系。LeetCo…...
Linux程序运行日志总结
在Linux系统中,程序运行时产生的日志记录主要通过以下几种方式实现,这些日志有助于排查问题、监控系统行为或审计安全事件: 1. 系统日志(System Logs) 存放路径:通常位于 /var/log/ 目录下。常见日志文件: /var/log/syslog 或 /var/log/messages:通用系统日志(取决于发…...
Perl One-liner 数据处理——基础语法篇【匠心】
Perl(Practical Extraction and Report Language)是一种功能强大且灵活的脚本语言,因其强大的文本处理能力和简洁的语法而广受开发者和系统管理员的喜爱。特别是在命令行环境下,Perl 的 one-liner(单行脚本)以其高效、简洁的特点,成为数据处理、文本转换和快速原型设计的…...