【设计模式】状态模式
文章目录
- 前言
- 状态模式
- 1、状态模式介绍
- 1.1 存在问题
- 1.2 解决问题
- 1.3 状态模式结构图
- 2、具体案例说明状态模式
- 2.1 不使用状态模式
- 2.2 使用状态模式
- 3、状态模式总结
前言
状态模式主要解决的是当控制一个对象状态转换的条件表达式过于复杂时的情况。把状态的判断逻辑转移到表示不同状态的一系列类当中,可以把复杂的判断逻辑简化。当然,如果这个状态判断很简单,那就没必要用状态模式了。
状态模式
1、状态模式介绍
状态模式(State)是一种行为型设计模式,当一个对象的内在状态改变时允许改变其行为,这个对象看起来像是改变了其类。(可以说当到饭点了,你就会主动去找饭吃)
1.1 存在问题
通常情况下,一个对象的行为会随着其内部状态的改变而发生变化,这些行为通常被包含在相应的if...else 语句中,导致代码难以维护和扩展。
1.2 解决问题
适用状态模式判断状态是否改变。
状态模式的思想是将每种可能的状态都封装成一个类,因此可以在不改变原有代码的前提下动态地改变对象的行为。状态模式提供了一种简单的实现方式,即通过定义一个state接口,再定义具体的state子类,Context类中持有一个state的引用,在不同状态下,分别委托给不同的state处理相应的请求。这种实现方式将原来的大类拆分成多个小类,使得系统更加灵活、易于扩展,符合开闭原则。
状态模式主要解决的是当控制一个对象状态转换的条件表达式过于复杂时的情况。把状态的判断逻辑转移到表示不同状态的一系列类当中,可以把复杂的判断逻辑简化。当然,如果这个状态判断很简单,那就没必要用状态模式了。
1.3 状态模式结构图

- State类,抽象状态类,定义一个接口以封装与Context的一个特定状态相关的行为。
/*** 抽象状态类*/
public abstract class State {public abstract void handle(Context context);
}
- Context类,维护一个ConcreteState子类的实例,这个实例定义当前的状态。
public class Context {private State state;/*** 初始化当前状态* @param state*/public Context(State state) {this.state = state;}public State getState() {return state;}public void setState(State state) {this.state = state;System.out.println("当前状态:"+this.state.getClass().getName());}/*** 对请求做处理,并设置下一个状态*/public void request(){this.state.handle(this);}
}
- ConcreteState类,具体状态,每一个子类实现一个与Context的一个状态相关的行为。
/*** 具体状态类A*/public class ConcreteStateA extends State{/*** 设置ConcreteStateA的下一个状态是ConcreteStateB* @param context*/@Overridepublic void handle(Context context) {context.setState(new ConcreteStateB());}
}
/*** 具体状态类B*/
public class ConcreteStateB extends State {/*** 设置ConcreteStateB的下一个状态是ConcreteStateA* @param context*/@Overridepublic void handle(Context context) {context.setState(new ConcreteStateA());}
}
- 客户端发起请求调用
public class ClientTest {public static void main(String[] args) {// 初始状态为ConcreteStateAContext context = new Context(new ConcreteStateA());// 不断请求,不断改变请求状态context.request();context.request();context.request();}
}
输出结果:

2、具体案例说明状态模式
案例:不同的工作时间做不同的事情
2.1 不使用状态模式
/*** 工作类*/public class Work {// 时间private int hour;// 是否完成工作任务private boolean workFinished = false;public int getHour() {return hour;}public void setHour(int hour) {this.hour = hour;}public boolean getWorkFinished() {return workFinished;}public void setWorkFinished(boolean workFinished) {this.workFinished = workFinished;}/*** 工作时间段*/public void workTime() {if (hour < 12) {System.out.println("当前时间" + hour + "点,上午工作,精神百倍。");} else if (hour < 13) {System.out.println("当前时间" + hour + "点,饿了,午饭,犯困,午休。");} else if (hour < 17) {System.out.println("当前时间" + hour + "点,下午状态还可以,继续敲代码。");} else {if (workFinished) {System.out.println("当前时间" + hour + "点,下班回家了!!,愉快结束一天。");} else {if (hour < 21) {System.out.println("当前时间" + hour + "点,又开始加班,疲累之极,**加班。");} else {System.out.println("当前时间" + hour + "点,睡觉时间到了,躺床就睡着。");}}}}
}
客户端
public class WorkClient {public static void main(String[] args) {Work work = new Work();// 早上work.setHour(9);work.workTime();work.setHour(11);work.workTime();// 中午work.setHour(12);work.workTime();//下午work.setHour(13);work.workTime();work.setHour(14);work.workTime();work.setHour(17);//工作未完成work.setWorkFinished(false);// 加班work.workTime();work.setHour(19);work.workTime();work.setHour(22);work.workTime();}
}
结果显示:

有没有发现什么问题?workTime()这个方法已经违背了开闭原则了,每次都要修改这个方法才得以扩展新的功能
2.2 使用状态模式
具体的结构类图

State类:
/*** 状态类*/
public abstract class State {public abstract void workTime(Work work);
}
Work工作类:
/*** 工作类*/
public class Work {/*** 时间点*/private int hour;/*** 是否完成工作任务-是否到达下班条件*/private boolean workFinished = false;/*** 当前状态-设置下一个状态*/private State currentState;/*** 初始化状态-上午*/public Work() {currentState = new ForenoonState();}/*** 工作时间段-显示当前状态,并切换到下一个状态*/public void workTime() {this.currentState.workTime(this);}public State getCurrentState() {return currentState;}public void setCurrentState(State currentState) {this.currentState = currentState;}public int getHour() {return hour;}public void setHour(int hour) {this.hour = hour;}public boolean getWorkFinished() {return workFinished;}public void setWorkFinished(boolean workFinished) {this.workFinished = workFinished;}
}
早上具体状态:
/*** 早上具体状态*/
public class ForenoonState extends State {@Overridepublic void workTime(Work work) {if (work.getHour() < 12) {System.out.println("当前时间" + work.getHour() + "点,上午工作,精神百倍。");} else {// 超过12点就转入中午状态work.setCurrentState(new NoonState());work.workTime();}}
}
中午具体状态:
/*** 中午状态*/
public class NoonState extends State {@Overridepublic void workTime(Work work) {if (work.getHour() < 13) {System.out.println("当前时间" + work.getHour() + "点,饿了,午饭,犯困,午休。");} else {// 超过13点就转入下午工作状态work.setCurrentState(new AfternoonState());work.workTime();}}
}
下午具体状态:
/*** 下午具体状态*/
public class AfternoonState extends State {@Overridepublic void workTime(Work work) {if (work.getHour() < 17) {System.out.println("当前时间" + work.getHour() + "点,下午状态还可以,继续敲代码。");} else {// 超时17点就进去傍晚工作时间点work.setCurrentState(new EveingState());work.workTime();}}
}
任务完成,按时下班状态类
/*** 按时下班*/
public class RestState extends State {@Overridepublic void workTime(Work work) {System.out.println("当前时间:" + work.getHour() + "点,下班回家咯!!");}
}
工作任务未完成,加班“累”:
/*** 具体加班类*/
public class EveingState extends State {@Overridepublic void workTime(Work work) {if (work.getWorkFinished()) {// 工作完成,下班work.setCurrentState(new RestState());work.workTime();} else {// 工作没有完成则继续加班if (work.getHour() < 21) {System.out.println("当前时间" + work.getHour() + "点,又开始加班,疲累之极,**加班。");} else {// 到点睡觉work.setCurrentState(new SleepingState());work.workTime();}}}
}
睡觉类:
/*** 睡觉状态*/
public class SleepingState extends State {@Overridepublic void workTime(Work work) {System.out.println("当前时间" + work.getHour() + "点,睡觉时间到了,躺床就睡着。");}
}
客户端同上
最终的结果也同上
虽然结果相同,但是我们的程序变得更加灵活,比如公司要求在20点之前必须离开公司, 此时我们就要新增一个“强制下班类”,并改动一下
“晚间工作状态”类的判断就可以 了。而这是不影响其他状态的代码的。
实现加班类修改如下:
/*** 具体加班类*/
public class EveingState extends State {@Overridepublic void workTime(Work work) {if (work.getWorkFinished()) {// 工作完成,下班work.setCurrentState(new RestState());work.workTime();} else {// 工作没有完成则继续加班work.setCurrentState(new ForcedLiveWork());work.workTime();}}
}
强制下班类:
/*** 强制下班*/
public class ForcedLiveWork extends State {@Overridepublic void workTime(Work work) {if (work.getHour() < 20) {System.out.println("当前时间" + work.getHour() + "点,公司规定,此刻必须要离开公司了。");} else {// 到点睡觉work.setCurrentState(new SleepingState());work.workTime();}}
}
实现起来并不困难,只需增加一个类,再对去修改判断条件,这样就不会影响到其他状态的代码
3、状态模式总结
状态模式的优点包括:
- 将状态转换和行为隔离开来,使得状态变化时只需要改变状态类对象即可,无需修改Context类,从而保证了系统的灵活性、可扩展性和可维护性。
- 在状态模式中,每个状态都被封装在一个类中,增加新的状态类很方便,符合开闭原则。
- 通过引入抽象状态类和抽象环境类,可以很好地解决代码的耦合问题。状态模式通过把各种状态转移逻辑分布到State的子类之间,来减少相互间的依赖
- 状态模式使得状态转换显式化了,独立于具体的状态类之外,更符合面向对象的设计思想。
- 对于状态机的实现,状态模式提供了一种设计思路和方法。
- 将特定的状态相关的行为都放入一个对象中,由于所有与 状态相关的代码都存在于某个ConcreteState中,所以通过定义新的子类可 以很容易地增加新的状态和转换。(消除大量的条件判断语句)
状态模式的缺点包括:
- 由于引入了多个子类,因此在一定程度上增加了系统的复杂度,使得系统抽象层次增加,设计难度加大。
- 如果状态改变很频繁,则会导致系统中类的数量增加,从而增加系统的维护难度。
- 如果状态比较多,且状态之间的转换比较复杂,容易造成代码的混乱和不易维护。
状态模式适用场景:
- 当一个对象的行为取决于它的状态,并且它必须在运行时刻根据状态 改变它的行为时,可以考虑使用状态模式。
- 当对象的行为随着对象内部状态的变化而发生变化时,可以考虑使用状态模式。例如,电视机有开机、关机、切换频道等状态,根据不同的状态做出相应的响应,使用状态模式可以方便地实现。
- 当系统中存在多种状态且状态之间存在转换关系时,可以考虑使用状态模式。例如,一个产品订单在待支付、已支付、已取消等多个状态之间转换,可以使用状态模式来实现。
- 当需要对状态进行动态修改时,可以考虑使用状态模式。例如,在游戏中,玩家角色根据当前的状态有不同的技能和装备,而这些状态是可以通过游戏中获得的物品进行修改的,这时候就可以使用状态模式。
- 当状态转换规则比较复杂或需要进行扩展时,可以考虑使用状态模式。状态模式将状态转换规则封装在具体状态类中,可以方便地对状态转换规则进行修改和扩展。
- 当希望避免使用大量的if else语句,提高代码可读性和可维护性时,可以考虑使用状态模式。状态模式使得代码易于扩展和修改,并且降低了代码的耦合度,更符合面向对象的设计原则。
相关文章:
【设计模式】状态模式
文章目录 前言状态模式1、状态模式介绍1.1 存在问题1.2 解决问题1.3 状态模式结构图 2、具体案例说明状态模式2.1 不使用状态模式2.2 使用状态模式 3、状态模式总结 前言 状态模式主要解决的是当控制一个对象状态转换的条件表达式过于复杂时的情况。把状态的判断逻辑转移到表示…...
内核驱动支持浮点数运算
最近在调 iio 下的 ICM42686 驱动,因项目求需要在驱动对加速度和陀螺raw数据进行换算,避免不了浮点运算。内核编译时出现了报错,提示如下: drivers/iio/imu/tdk_icm42686/icm42686.o: In function gyro_data2float: /home/share/…...
Flink学习(一)
分布式计算框架 Java可以使用分布式计算来处理大规模的数据和计算任务,提高计算效率和性能。以下是一些Java分布式计算的例子: Apache Hadoop:Hadoop是一个开源的分布式计算框架,可以处理大规模数据集的分布式存储和处理。它使用Java编写,可以在分布式环境中运行MapReduc…...
linux 常用命令awk
AWK 是一种处理文本文件的语言,是一个强大的文本分析工具。之所以叫 AWK 是因为其取了三位创始人 Alfred Aho,Peter Weinberger, 和 Brian Kernighan 的 Family Name 的首字符。 AWK用法 awk 用法:awk pattern {action} files 1.RS, ORS, F…...
MySQL学习---15、流程控制、游标
1、流程控制 解决复杂问题不可能是通过一个SQL语句完成,我们需要执行多个SQL操作。流程控制语句的作用就是控制存储过程中SQL语句的执行顺序,是我们完成复杂操作必不可少的一部分。只要是执行的程序,流程就分为三大类: 1、顺序结…...
信息调查的观念
每次做一件事前都要把这件事调查清楚,比如考一门科目我们要把和这门科目有关的资源都收集起来,然后把再从中筛选出有用的信息,如数值计算方法我们在考试前就可以把b站有关的学习资源网课或者前人总结的考试经验做个收集总结,做出对…...
leetcode 337. 打家劫舍 III
题目链接:leetcode 337 1.题目 小偷又发现了一个新的可行窃的地区。这个地区只有一个入口,我们称之为 root 。 除了 root 之外,每栋房子有且只有一个“父“房子与之相连。一番侦察之后,聪明的小偷意识到“这个地方的所有房屋的…...
基于Docker的深度学习环境NVIDIA和CUDA部署以及WSL和linux镜像问题
基于Docker的深度学习环境部署 1. 什么是Docker?2. 深度学习环境的基本要求3. Docker的基本操作3.1 在Windows上安装Docker3.2 在Ubuntu上安装Docker3.3 拉取一个pytorch的镜像3.4 部署自己的项目3.5 导出配置好项目的新镜像 4. 分享新镜像4.1 将镜像导出为tar分享给…...
c#中slice,substr,substring区别
1. 都使用一个参数: //栗子数据 var arr [1,2,3,4,5,6,7], str "helloworld!"; //防止空格干扰,不用带空格的,注意这里有个!号也算一位 console.log(str.slice(1)); //elloworld! console.log(str.substring(1)); //…...
java语言里redis在项目中使用场景,每个场景的样例代码
Redis是一款高性能的NoSQL数据库,常被用于缓存、消息队列、计数器、分布式锁等场景。以下是50个Redis在项目中使用的场景以及对应的样例代码和详细说明: ##1、缓存:将查询结果缓存在Redis中,下次查询时直接从缓存中获取ÿ…...
Mongo集合操作
2、创建切换数据库 2.1 默认数据库 mongo数据库和其他类型的数据库一样,可以创建数据库,且可以创建多个数据库。 mongo数据库默认会有四个数据库,分别是 admin:主要存储MongoDB的用户、角色等信息 config:主要存储…...
ConvTranspose2d 的简单例子理解
文章目录 参考基础概念output_padding 简单例子: stride2step1step2step3 参考 逆卷积的详细解释ConvTranspose2d(fractionally-strided convolutions)nn.ConvTranspose2d的参数output_padding的作用torch.nn.ConvTranspose2d Explained 基础概念 逆卷…...
酒精和肠内外健康:有帮助还是有害?
谷禾健康 酒精与健康 饮酒作为一种特殊的文化形式,在我们国家有其独特的地位,在几千年的发展中,酒几乎渗透到日常生活、社会经济、文化活动之中。 据2018年发表的《中国饮酒人群适量饮酒状况》白皮书数据显示,中国饮酒人群高达6亿…...
SylixOS Shell下操作环境变量方法
系统启动后会在内核中生成一份默认的环境变量,环境变量名和默认值由源程序决定。系统启动后如果文件系统中存在有效的/etc/profile文件,则还会自动读取文件中的内容,并导入到Shell环境中,覆盖对应变量或增加新的变量。程序运行时&…...
【dfs解决分组问题-两道例题——供佬学会!】(A元素是放在已经存在的组别中,还是再创建一个更好?--小孩子才做选择,dfs直接两种情况都试试)
问题关键就是: 一个点,可能 新开一个组 比 放到已经存在的组 更划算 因为后面的数据,我们遍历之前的点时,并不知道 所以我们应该针对每个点,都应该做出一个选择就是 新开一个元组或者放到之前的元组中,都尝…...
使用Hexo在Github上搭建个人博客
使用Hexo在Github上搭建个人博客 1. 安装Node和git2. 安装Hexo3. Git与Github的准备工作4. 将Hexo部署到Github5. 开始写作 1. 安装Node和git 在Mac上安装Node.js可以使用Homebrew,使用以下命令安装: brew install node使用以下命令安装Git: …...
【面试题】面试官:说说你对 CSS 盒模型的理解
前言 CSS 盒模型是 CSS 基础的重点难点,因此常被面试官们拿来考察候选人对前端基础的掌握程度,这篇文章将对 CSS 盒模型知识点进行全面的梳理。 我们先看个例子:下面的 div 元素的总宽度是多少呢? js <!DOCTYPE html> &…...
【ROS2】学习笔记
1. 基础概念 1.1 执行单元 1.1.1 executable——执行程序 executable表示针对某个目标的程序执行流程,一个executable可以启动多个node; 1.1.2 node——“进程” node其实就是进程的意思; ROS2允许同时启动两个相同的node,&a…...
Springboot +Flowable,流程表单应用之外置表单(JSON形式)(二)
一.简介 整体上来说,我们可以将Flowable 的表单分为三种不同的类型: 动态表单 这种表单定义方式我们可以配置表单中每一个字段的可读性、可写性、是否必填等信息,不过不能定义完整的表单页面。外置表单 外置表单我们只需要定义一下表单的 k…...
JavaScript如何使用if语句
JavaScript的if语句可以让我们根据某些条件来执行不同的代码块。使用if语句的基本思路是将要执行的代码放在括号内,并使用if关键字进行匹配。下面是一些例子: 简单的if语句: let age 18; if (age > 18) { console.log("You are…...
日语AI面试高效通关秘籍:专业解读与青柚面试智能助攻
在如今就业市场竞争日益激烈的背景下,越来越多的求职者将目光投向了日本及中日双语岗位。但是,一场日语面试往往让许多人感到步履维艰。你是否也曾因为面试官抛出的“刁钻问题”而心生畏惧?面对生疏的日语交流环境,即便提前恶补了…...
python打卡day49
知识点回顾: 通道注意力模块复习空间注意力模块CBAM的定义 作业:尝试对今天的模型检查参数数目,并用tensorboard查看训练过程 import torch import torch.nn as nn# 定义通道注意力 class ChannelAttention(nn.Module):def __init__(self,…...
ssc377d修改flash分区大小
1、flash的分区默认分配16M、 / # df -h Filesystem Size Used Available Use% Mounted on /dev/root 1.9M 1.9M 0 100% / /dev/mtdblock4 3.0M...
Robots.txt 文件
什么是robots.txt? robots.txt 是一个位于网站根目录下的文本文件(如:https://example.com/robots.txt),它用于指导网络爬虫(如搜索引擎的蜘蛛程序)如何抓取该网站的内容。这个文件遵循 Robots…...
Axios请求超时重发机制
Axios 超时重新请求实现方案 在 Axios 中实现超时重新请求可以通过以下几种方式: 1. 使用拦截器实现自动重试 import axios from axios;// 创建axios实例 const instance axios.create();// 设置超时时间 instance.defaults.timeout 5000;// 最大重试次数 cons…...
A2A JS SDK 完整教程:快速入门指南
目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库ÿ…...
排序算法总结(C++)
目录 一、稳定性二、排序算法选择、冒泡、插入排序归并排序随机快速排序堆排序基数排序计数排序 三、总结 一、稳定性 排序算法的稳定性是指:同样大小的样本 **(同样大小的数据)**在排序之后不会改变原始的相对次序。 稳定性对基础类型对象…...
Netty从入门到进阶(二)
二、Netty入门 1. 概述 1.1 Netty是什么 Netty is an asynchronous event-driven network application framework for rapid development of maintainable high performance protocol servers & clients. Netty是一个异步的、基于事件驱动的网络应用框架,用于…...
Python Einops库:深度学习中的张量操作革命
Einops(爱因斯坦操作库)就像给张量操作戴上了一副"语义眼镜"——让你用人类能理解的方式告诉计算机如何操作多维数组。这个基于爱因斯坦求和约定的库,用类似自然语言的表达式替代了晦涩的API调用,彻底改变了深度学习工程…...
OD 算法题 B卷【正整数到Excel编号之间的转换】
文章目录 正整数到Excel编号之间的转换 正整数到Excel编号之间的转换 excel的列编号是这样的:a b c … z aa ab ac… az ba bb bc…yz za zb zc …zz aaa aab aac…; 分别代表以下的编号1 2 3 … 26 27 28 29… 52 53 54 55… 676 677 678 679 … 702 703 704 705;…...
