设计模式(二十一)----行为型模式之状态模式
1 概述
【例】通过按钮来控制一个电梯的状态,一个电梯有开门状态,关门状态,停止状态,运行状态。每一种状态改变,都有可能要根据其他状态来更新处理。例如,如果电梯门现在处于运行时状态,就不能进行开门操作,而如果电梯门是停止状态,就可以执行开门操作。
类图如下:

代码如下:
public interface ILift {//电梯的4个状态//开门状态public final static int OPENING_STATE = 1;//关门状态public final static int CLOSING_STATE = 2;//运行状态public final static int RUNNING_STATE = 3;//停止状态public final static int STOPPING_STATE = 4;
//设置电梯的状态public void setState(int state);
//电梯的动作public void open();public void close();public void run();public void stop();
}
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;}}
}
public class Client {public static void main(String[] args) {Lift lift = new Lift();lift.setState(ILift.STOPPING_STATE);//电梯是停止的lift.open();//开门lift.close();//关门lift.run();//运行lift.stop();//停止}
}
问题分析:
-
使用了大量的switch…case这样的判断(if…else也是一样),使程序的可阅读性变差。
-
扩展性很差。如果新加了断电的状态,我们需要修改上面判断逻辑
定义:
对有状态的对象,把复杂的“判断逻辑”提取到不同的状态对象中,允许状态对象在其内部状态发生改变时改变其行为。
2 结构
状态模式包含以下主要角色。
-
环境(Context)角色:也称为上下文,它定义了客户程序需要的接口,维护一个当前状态,并将与状态相关的操作委托给当前状态对象来处理。
-
抽象状态(State)角色:定义一个接口,用以封装环境对象中的特定状态所对应的行为。
-
具体状态(Concrete State)角色:实现抽象状态所对应的行为。
3 案例实现
对上述电梯的案例使用状态模式进行改进。类图如下:

代码如下:
//抽象状态类
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();
}
//开启状态
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();}
}
//环境角色
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();}
}
测试结果

4 优缺点
1,优点:
-
将所有与某个状态有关的行为放到一个类中,并且可以方便地增加新的状态,只需要改变对象状态即可改变对象的行为。
-
允许状态转换逻辑与状态对象合成一体,而不是某一个巨大的条件语句块。
2,缺点:
-
状态模式的使用必然会增加系统类和对象的个数。
-
状态模式的结构与实现都较为复杂,如果使用不当将导致程序结构和代码的混乱。
-
状态模式对"开闭原则"的支持并不太好。
5 使用场景
-
当一个对象的行为取决于它的状态,并且它必须在运行时根据状态改变它的行为时,就可以考虑使用状态模式。
-
一个操作中含有庞大的分支结构,并且这些分支决定于对象的状态时。
相关文章:
设计模式(二十一)----行为型模式之状态模式
1 概述 【例】通过按钮来控制一个电梯的状态,一个电梯有开门状态,关门状态,停止状态,运行状态。每一种状态改变,都有可能要根据其他状态来更新处理。例如,如果电梯门现在处于运行时状态,就不能…...
一分钟理解 AP(Affinity Propagation) 亲和⼒传播算法
从来没有一个算法让我研究好几天都搞不明白,AP算法算是第一个。弄了好几天,打草纸用了几十页,反复琢磨,最后都怀疑人生了。我觉得网上那么多介绍 AP 的文章,基本上没有一篇能讲明白的。最后我都觉得 AP 的作者可能都没…...
使用mybatis的映射文件操作存储过程
先随便创建一个存储过程 DELIMITER $$ CREATE PROCEDURE getUserNameById (IN i_id BIGINT, OUT o_name VARCHAR(10)) BEGINSELECT u.name INTO o_name FROM tb_user u WHERE id i_id; END $$delimiter $$ : 是将sql语句的结束符号先替换成$$的意思,因为sql是遇到…...
世界上最完美的两个软件,太厉害了!
今天给大家介绍两个软件,一个体现了人类在软件开发流程上的极致,另外一个则体现了程序员个体能力的巅峰。01航天飞机飞控软件先来说第一个,航天飞机飞行控制软件,就是下图这个大家伙。航天飞机重达120吨,还携带着2000吨…...
教你成为比卡卡西还牛逼的全能忍者,全拷贝与分割函数
如何成为一个集雷切,写轮眼侦查和拷贝与一身的卡卡西,下面教你! 目录 第一式——雷切! strtok 第二式——写轮眼侦查! strerror函数 第三式——写轮眼拷贝! memcpy 模拟实现memcpy函数 😎…...
【LeetCode】剑指 Offer(24)
目录 题目:剑指 Offer 47. 礼物的最大价值 - 力扣(Leetcode) 题目的接口: 解题思路: 代码: 过啦!!! 写在最后: 题目:剑指 Offer 47. 礼物的…...
javaEE 初阶 — CSS 元素的显示模式与盒模型
文章目录1. 元素的显示模式1.1 块级元素1.2 行内元素1.3 行内元素和块级元素的区别1.4 改变显示模式2. 盒模型2.1 边框2.1.1 边框的粗细2.1.2 边框的颜色2.1.3 边框的风格2.2 内边距2.3 外边距2.3.1 margin 的特殊情况1. 元素的显示模式 1.1 块级元素 常见的元素: h1 - h6 、…...
新星计划-我为什么要写博客?写博客的意义是什么
CSDN的各位友友们你们好,今天千泽要和大家交流一下写博客的意义,并且鼓励大家参加CSDN官方举办的新星计划,这个可以让我们更快的成长,十分有价值.接下来让我们一起开始吧!如果对您有帮助的话希望能够得到您的支持和帮助,我会持续更新的!🚩part1:自我介绍我是一名来自…...
嵌入式学习笔记——STM32的USART收发字符串及串口中断
USART收发字符串及串口中断前言字符串的收发发送一个字符串接收字符串需求利用串口实现printf中断中断是什么前言 上一篇中,介绍了串口收发相关的寄存器,通过代码实现了一个字节的收发,本文接着上面的内容,通过功能函数实现字符串…...
数据分析之Pandas(1)
3.Pandas 文章目录3.Pandas3.1 Pandas基本介绍3.1.1 Pandas的基本数据结构3.1.1.1 Pandas库的Series类型3.1.1.2 Pandas库的DataFrame类型DataFrame初始化DataFrame查看数据3.1.2 Pandas读取数据及数据操作行操作添加一行删除一行列操作增加一列删除一列通过标签选择数据条件选…...
17、江科大stm32视频学习笔记——USART串口协议和USART串口外设
目录 1、通信接口 2、 硬件电路 3、电平标准 4、串口参数及时序 5、USART简介 6、USART工作 (1)写操作 (2)读操作 (3)帧头和帧尾的添加和除由电路自动执行 (4)硬件数据控制…...
leetcode:有效地括号
给定一个只包括 ‘(’,‘)’,‘{’,‘}’,‘[’,‘]’ 的字符串 s ,判断字符串是否有效。 有效字符串需满足: 左括号必须用相同类型的右括号闭合。 左括号必须以正确的顺序闭合。 每个右括号都…...
四等分list
Testpublic void s (){ int targ 4; List mList new ArrayList(); List<List> mEndList new ArrayList<>(); for (int i 0; i <34; i) { mList.add(“item” i); } // System.out.println(mList.toString()); if (mList.size() % targ ! 0) { for (int j …...
php连接sqlserver数据库
docker 安装sqlserver数据库sudo docker pull mcr.microsoft.com/mssql/server:2017-latestsudo docker run -e "ACCEPT_EULAY" -e "MSSQL_SA_PASSWORD<YourStrongPassw0rd>" -p 1433:1433 --name sqlserver --hostname sqlserver -d mcr.microsoft.…...
The 2019 China Collegiate Programming Contest Harbin Site F. Fixing Banners
Problem - F - Codeforces 翻译: 哈尔滨,这个名字最初是一个满语单词,意思是“晒渔网的地方”,从松花江边的一个小农村居民点发展成为中国东北最大的城市之一。1898年,随着中国东部铁路的到来,这座城市首先…...
Maven的下载和配置
一、前言 一般按要求下载jar ,但是jar 包版本不好控制。有时候就会jar版本不同导致项目运行的结果也有差异,这样在团队开发中,在多个项目开发的是,jar包还要进行拷贝,可能也会出现版本还jar损坏的情况,所以一个能统一…...
服务高并发、高性能、高可用实现方案
服务高并发、高性能、高可用实现方案 软件开发的三高指标:高并发、高性能、高可用。 高并发方面要求QPS 大于 10万;高性能方面要求请求延迟小于 100 ms;高可用方面要高于 99.99%(4个9) 一、高并发: 高并发是现在互联网分布式框架设…...
uniCloud在线升级APP配置教程
app在线升级背景实现思路流程流程背景 因用户需要添加手机h5页面来进数据操作实现思路流程 实现流程图流程 相关文档:帮助文档 https://uniapp.dcloud.net.cn/uniCloud/cf-functions.html 注册服务空间 https://unicloud.dcloud.net.cn/pages/login/login uni升级…...
idea常用的快捷键
idea常用的快捷键Alt回车 导入包,自动修正CtrlN 查找类CtrlShiftN 查找文件CtrlAltL 格式化代码CtrlAltO 优化导入的类和包AltInsert 生成代码(如get,set方法,构造函数等)CtrlE或者AltShiftC 最近更改的代码CtrlR 替换文本CtrlF 查找文本CtrlShiftSpace 自动补全代码Ctrl空格 代…...
全志V85x硬件设计大赛作品精选第一期,快来Pick你心目中的最佳方案
1、V853-智能交互摄像头开发板 该参赛作品基于全志V853开发板制作的一款类似眼镜外挂的小产品,可以对场景进行辅助识别,并通过云端交互实现物联网控制,进一步实现物联网与人机交互的融合。 开发板配置了摄像头和小屏幕接口,并外…...
MicroClaw:跨平台智能体运行时,统一AI助手部署与管理
1. 项目概述:一个跨平台的智能体运行时如果你曾经尝试过在不同的聊天平台上部署AI助手,比如在Telegram上搞一个,又在Discord上搞一个,你大概率会感到头疼。每个平台都有自己的一套API、认证方式和消息格式,这意味着你几…...
STM32高效驱动WS2812:SPI+DMA时序精解与实战避坑
1. WS2812驱动原理与SPIDMA方案优势 第一次接触WS2812灯带时,我被它的单线控制方式惊艳到了——只需要一根信号线就能控制数百个RGB灯珠。但真正动手实现时才发现,这个看似简单的协议背后藏着不少玄机。WS2812采用归零码(RZ)编码方…...
如何用自动化脚本解放双手:淘宝淘金币全任务一键完成指南
如何用自动化脚本解放双手:淘宝淘金币全任务一键完成指南 【免费下载链接】taojinbi 淘宝淘金币自动执行脚本,包含蚂蚁森林收取能量,芭芭农场全任务,解放你的双手 项目地址: https://gitcode.com/gh_mirrors/ta/taojinbi 还…...
VINS-Mono在EUROC数据集上的实战评测:从轨迹精度到运行耗时,我的避坑心得
VINS-Mono在EUROC数据集上的实战评测:从轨迹精度到运行耗时,我的避坑心得 当第一次在无人机上部署VINS-Mono时,我盯着实时轨迹和地面真值之间逐渐拉大的偏差,意识到论文里的漂亮曲线背后藏着太多未言明的细节。这次评测源于一个实…...
LaTeX排版避坑指南:为什么你的多图caption编号对不齐?一个案例讲清subfig、minipage和tabular的选择
LaTeX多图排版实战:从编号错乱到完美对齐的解决方案 第一次在学术论文中遇到多图排版问题时,我盯着那个歪歪扭扭的(c)编号看了整整十分钟——它本该和(a)(b)整齐排列,却像迷路的孩子一样缩在角落。更糟的是,当我引用这些子图时&am…...
Cursor AI技能库一键部署指南:提升开发效率的标准化配置方案
1. 项目概述:当AI助手Cursor遇上Everything技能库如果你和我一样,日常开发重度依赖Cursor这款AI驱动的IDE,那你肯定也遇到过这样的场景:想让它帮你写个单元测试,得先花几分钟描述TDD流程;想让它重构一段代码…...
德尔·考德威尔:从微波校准到计量标准,塑造现代精密测量的隐形基石
1. 一位计量学巨匠的遗产:从德尔考德威尔看精密测量的基石在电子工程与测试测量这个庞大而精密的领域里,我们常常关注的是最新的示波器带宽、最前沿的矢量网络分析技术,或是某个芯片的测试方案。然而,支撑起整个现代工业测量体系可…...
Python包安装全攻略:从pip、conda到离线安装,总有一种方法适合你
Python包安装全攻略:从pip、conda到离线安装,总有一种方法适合你 在Python开发中,依赖管理是每个开发者必须掌握的核心技能。无论是数据科学家搭建机器学习环境,还是Web开发者部署Django应用,都离不开Python包的安装与…...
从零构建RAG应用:LLM+向量数据库实战指南与调优心得
1. 从零到一:我的生成式AI学习路径与实战心得最近几年,生成式AI(Generative AI)的浪潮席卷了几乎所有行业,从能写代码的Copilot到能画图的Midjourney,再到能对话的ChatGPT,感觉一夜之间…...
手把手教你搞定Sx1262射频前端:从天线匹配到LPF滤波的完整电路设计(附PCB布局建议)
手把手教你搞定Sx1262射频前端:从天线匹配到LPF滤波的完整电路设计(附PCB布局建议) 在物联网设备开发中,射频前端设计往往是硬件工程师最头疼的环节之一。特别是使用Semtech的Sx1262这类LoRa芯片时,一个设计不当的射频…...
