当前位置: 首页 > news >正文

【设计模式】状态模式

文章目录

  • 前言
  • 状态模式
  • 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 状态模式结构图

在这里插入图片描述

  1. State类,抽象状态类,定义一个接口以封装与Context的一个特定状态相关的行为。
/*** 抽象状态类*/
public abstract class State {public abstract void  handle(Context context);
}
  1. 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);}
}
  1. 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());}
}
  1. 客户端发起请求调用
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、状态模式总结

状态模式的优点包括:

  1. 将状态转换和行为隔离开来,使得状态变化时只需要改变状态类对象即可,无需修改Context类,从而保证了系统的灵活性、可扩展性和可维护性。
  2. 在状态模式中,每个状态都被封装在一个类中,增加新的状态类很方便,符合开闭原则。
  3. 通过引入抽象状态类和抽象环境类,可以很好地解决代码的耦合问题。状态模式通过把各种状态转移逻辑分布到State的子类之间,来减少相互间的依赖
  4. 状态模式使得状态转换显式化了,独立于具体的状态类之外,更符合面向对象的设计思想。
  5. 对于状态机的实现,状态模式提供了一种设计思路和方法。
  6. 将特定的状态相关的行为都放入一个对象中,由于所有与 状态相关的代码都存在于某个ConcreteState中,所以通过定义新的子类可 以很容易地增加新的状态和转换。(消除大量的条件判断语句)

状态模式的缺点包括:

  1. 由于引入了多个子类,因此在一定程度上增加了系统的复杂度,使得系统抽象层次增加,设计难度加大。
  2. 如果状态改变很频繁,则会导致系统中类的数量增加,从而增加系统的维护难度。
  3. 如果状态比较多,且状态之间的转换比较复杂,容易造成代码的混乱和不易维护。

状态模式适用场景:

  1. 当一个对象的行为取决于它的状态,并且它必须在运行时刻根据状态 改变它的行为时,可以考虑使用状态模式。
  2. 当对象的行为随着对象内部状态的变化而发生变化时,可以考虑使用状态模式。例如,电视机有开机、关机、切换频道等状态,根据不同的状态做出相应的响应,使用状态模式可以方便地实现。
  3. 当系统中存在多种状态且状态之间存在转换关系时,可以考虑使用状态模式。例如,一个产品订单在待支付、已支付、已取消等多个状态之间转换,可以使用状态模式来实现。
  4. 当需要对状态进行动态修改时,可以考虑使用状态模式。例如,在游戏中,玩家角色根据当前的状态有不同的技能和装备,而这些状态是可以通过游戏中获得的物品进行修改的,这时候就可以使用状态模式。
  5. 当状态转换规则比较复杂或需要进行扩展时,可以考虑使用状态模式。状态模式将状态转换规则封装在具体状态类中,可以方便地对状态转换规则进行修改和扩展。
  6. 当希望避免使用大量的if else语句,提高代码可读性和可维护性时,可以考虑使用状态模式。状态模式使得代码易于扩展和修改,并且降低了代码的耦合度,更符合面向对象的设计原则。

相关文章:

【设计模式】状态模式

文章目录 前言状态模式1、状态模式介绍1.1 存在问题1.2 解决问题1.3 状态模式结构图 2、具体案例说明状态模式2.1 不使用状态模式2.2 使用状态模式 3、状态模式总结 前言 状态模式主要解决的是当控制一个对象状态转换的条件表达式过于复杂时的情况。把状态的判断逻辑转移到表示…...

内核驱动支持浮点数运算

最近在调 iio 下的 ICM42686 驱动&#xff0c;因项目求需要在驱动对加速度和陀螺raw数据进行换算&#xff0c;避免不了浮点运算。内核编译时出现了报错&#xff0c;提示如下&#xff1a; drivers/iio/imu/tdk_icm42686/icm42686.o: In function gyro_data2float: /home/share/…...

Flink学习(一)

分布式计算框架 Java可以使用分布式计算来处理大规模的数据和计算任务,提高计算效率和性能。以下是一些Java分布式计算的例子: Apache Hadoop:Hadoop是一个开源的分布式计算框架,可以处理大规模数据集的分布式存储和处理。它使用Java编写,可以在分布式环境中运行MapReduc…...

linux 常用命令awk

AWK 是一种处理文本文件的语言&#xff0c;是一个强大的文本分析工具。之所以叫 AWK 是因为其取了三位创始人 Alfred Aho&#xff0c;Peter Weinberger, 和 Brian Kernighan 的 Family Name 的首字符。 AWK用法 awk 用法&#xff1a;awk pattern {action} files 1.RS, ORS, F…...

MySQL学习---15、流程控制、游标

1、流程控制 解决复杂问题不可能是通过一个SQL语句完成&#xff0c;我们需要执行多个SQL操作。流程控制语句的作用就是控制存储过程中SQL语句的执行顺序&#xff0c;是我们完成复杂操作必不可少的一部分。只要是执行的程序&#xff0c;流程就分为三大类&#xff1a; 1、顺序结…...

信息调查的观念

每次做一件事前都要把这件事调查清楚&#xff0c;比如考一门科目我们要把和这门科目有关的资源都收集起来&#xff0c;然后把再从中筛选出有用的信息&#xff0c;如数值计算方法我们在考试前就可以把b站有关的学习资源网课或者前人总结的考试经验做个收集总结&#xff0c;做出对…...

leetcode 337. 打家劫舍 III

题目链接&#xff1a;leetcode 337 1.题目 小偷又发现了一个新的可行窃的地区。这个地区只有一个入口&#xff0c;我们称之为 root 。 除了 root 之外&#xff0c;每栋房子有且只有一个“父“房子与之相连。一番侦察之后&#xff0c;聪明的小偷意识到“这个地方的所有房屋的…...

基于Docker的深度学习环境NVIDIA和CUDA部署以及WSL和linux镜像问题

基于Docker的深度学习环境部署 1. 什么是Docker&#xff1f;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. 都使用一个参数&#xff1a; //栗子数据 var arr [1,2,3,4,5,6,7], str "helloworld!"; //防止空格干扰&#xff0c;不用带空格的&#xff0c;注意这里有个&#xff01;号也算一位 console.log(str.slice(1)); //elloworld! console.log(str.substring(1)); //…...

java语言里redis在项目中使用场景,每个场景的样例代码

Redis是一款高性能的NoSQL数据库&#xff0c;常被用于缓存、消息队列、计数器、分布式锁等场景。以下是50个Redis在项目中使用的场景以及对应的样例代码和详细说明&#xff1a; ##1、缓存&#xff1a;将查询结果缓存在Redis中&#xff0c;下次查询时直接从缓存中获取&#xff…...

Mongo集合操作

2、创建切换数据库 2.1 默认数据库 mongo数据库和其他类型的数据库一样&#xff0c;可以创建数据库&#xff0c;且可以创建多个数据库。 mongo数据库默认会有四个数据库&#xff0c;分别是 admin&#xff1a;主要存储MongoDB的用户、角色等信息 config&#xff1a;主要存储…...

ConvTranspose2d 的简单例子理解

文章目录 参考基础概念output_padding 简单例子&#xff1a; stride2step1step2step3 参考 逆卷积的详细解释ConvTranspose2d&#xff08;fractionally-strided convolutions)nn.ConvTranspose2d的参数output_padding的作用torch.nn.ConvTranspose2d Explained 基础概念 逆卷…...

酒精和肠内外健康:有帮助还是有害?

谷禾健康 酒精与健康 饮酒作为一种特殊的文化形式&#xff0c;在我们国家有其独特的地位&#xff0c;在几千年的发展中&#xff0c;酒几乎渗透到日常生活、社会经济、文化活动之中。 据2018年发表的《中国饮酒人群适量饮酒状况》白皮书数据显示&#xff0c;中国饮酒人群高达6亿…...

SylixOS Shell下操作环境变量方法

系统启动后会在内核中生成一份默认的环境变量&#xff0c;环境变量名和默认值由源程序决定。系统启动后如果文件系统中存在有效的/etc/profile文件&#xff0c;则还会自动读取文件中的内容&#xff0c;并导入到Shell环境中&#xff0c;覆盖对应变量或增加新的变量。程序运行时&…...

【dfs解决分组问题-两道例题——供佬学会!】(A元素是放在已经存在的组别中,还是再创建一个更好?--小孩子才做选择,dfs直接两种情况都试试)

问题关键就是&#xff1a; 一个点&#xff0c;可能 新开一个组 比 放到已经存在的组 更划算 因为后面的数据&#xff0c;我们遍历之前的点时&#xff0c;并不知道 所以我们应该针对每个点&#xff0c;都应该做出一个选择就是 新开一个元组或者放到之前的元组中&#xff0c;都尝…...

使用Hexo在Github上搭建个人博客

使用Hexo在Github上搭建个人博客 1. 安装Node和git2. 安装Hexo3. Git与Github的准备工作4. 将Hexo部署到Github5. 开始写作 1. 安装Node和git 在Mac上安装Node.js可以使用Homebrew&#xff0c;使用以下命令安装&#xff1a; brew install node使用以下命令安装Git&#xff1a; …...

【面试题】面试官:说说你对 CSS 盒模型的理解

前言 CSS 盒模型是 CSS 基础的重点难点&#xff0c;因此常被面试官们拿来考察候选人对前端基础的掌握程度&#xff0c;这篇文章将对 CSS 盒模型知识点进行全面的梳理。 我们先看个例子&#xff1a;下面的 div 元素的总宽度是多少呢&#xff1f; js <!DOCTYPE html> &…...

【ROS2】学习笔记

1. 基础概念 1.1 执行单元 1.1.1 executable——执行程序 executable表示针对某个目标的程序执行流程&#xff0c;一个executable可以启动多个node&#xff1b; 1.1.2 node——“进程” node其实就是进程的意思&#xff1b; ROS2允许同时启动两个相同的node&#xff0c;&a…...

Springboot +Flowable,流程表单应用之外置表单(JSON形式)(二)

一.简介 整体上来说&#xff0c;我们可以将Flowable 的表单分为三种不同的类型&#xff1a; 动态表单 这种表单定义方式我们可以配置表单中每一个字段的可读性、可写性、是否必填等信息&#xff0c;不过不能定义完整的表单页面。外置表单 外置表单我们只需要定义一下表单的 k…...

JavaScript如何使用if语句

JavaScript的if语句可以让我们根据某些条件来执行不同的代码块。使用if语句的基本思路是将要执行的代码放在括号内&#xff0c;并使用if关键字进行匹配。下面是一些例子&#xff1a; 简单的if语句&#xff1a; let age 18; if (age > 18) { console.log("You are…...

深度学习从心电信号中解码呼吸频率:原理、实现与临床价值

1. 项目概述&#xff1a;从心电信号中“听”到呼吸声呼吸频率&#xff0c;这个我们每分钟都在进行却很少被精确量化的生命体征&#xff0c;在临床医学中扮演着至关重要的角色。它不仅是评估呼吸系统功能的直接指标&#xff0c;更是反映全身代谢、循环乃至神经系统状态的“窗口”…...

蓝牙抓包不求人:从HCI日志里‘挖’出Link Key的两种实用方法(附安卓路径)

蓝牙安全逆向实战&#xff1a;从HCI日志中提取Link Key的深度解析在蓝牙协议安全研究领域&#xff0c;Link Key作为设备配对认证的核心凭证&#xff0c;其获取方式一直是逆向工程师关注的焦点。许多安全审计场景下&#xff0c;我们往往只能获得加密后的HCI通信日志&#xff0c;…...

量子计算中Loschmidt回声相位测量的创新方法

1. 量子计算中的Loschmidt回声相位测量方法概述Loschmidt回声是量子动力学中一个重要的概念&#xff0c;它描述了量子系统在时间反演演化后与初始状态的相似程度。在量子计算领域&#xff0c;精确测量Loschmidt回声的相位信息对于理解量子系统的非平衡态行为、计算能量本征值以…...

别再用SonarQube凑数了!DeepSeek原生圈复杂度引擎的6大颠覆性能力(含GitHub私有部署密钥)

更多请点击&#xff1a; https://kaifayun.com 第一章&#xff1a;DeepSeek圈复杂度分析的底层原理与范式革命 DeepSeek圈复杂度分析并非传统McCabe度量的简单复刻&#xff0c;而是基于控制流图&#xff08;CFG&#xff09;动态重构与语义感知路径裁剪的双重机制构建的新范式。…...

账务台账数据

银行里说的 “账务台账数据”&#xff0c;本质就是按会计规则把每笔业务逐笔、分户、分科目记下来的完整明细流水 余额 辅助信息&#xff0c;核心是 “可逐笔追溯、可对账、可审计” 的一套明细数据。下面用通俗、具体的方式拆开说&#xff1a;一、银行 “账务台账” 到底是什…...

CSharpVerbalExpressions常见问题解答:解决开发者遇到的10个典型挑战

CSharpVerbalExpressions常见问题解答&#xff1a;解决开发者遇到的10个典型挑战 【免费下载链接】CSharpVerbalExpressions 项目地址: https://gitcode.com/gh_mirrors/cs/CSharpVerbalExpressions CSharpVerbalExpressions是一个强大的C#库&#xff0c;它通过类自然语…...

二十六.签名与脚本(1)--脚本介绍

1.区块链脚本介绍在之前的章节中&#xff0c;我们了解了签名与验证相关&#xff0c;但是btc的交易数据&#xff0c;签名和验证&#xff0c;不是单纯的&#xff0c;还有脚本深度参与其中。我们从开始来&#xff1a;bool SendMoney(CScript scriptPubKey, int64 nValue, CWalletT…...

为什么你的DeepSeek微调loss震荡不止?(Meta/DeepSeek联合团队未公开的梯度裁剪+LoRA初始化双校准协议)

更多请点击&#xff1a; https://codechina.net 第一章&#xff1a;DeepSeek微调loss震荡的根本归因剖析 DeepSeek系列模型在微调过程中频繁出现loss剧烈震荡现象&#xff0c;其本质并非单一因素所致&#xff0c;而是数据、优化器、梯度动态与模型结构四者耦合失稳的系统性表现…...

对比自行维护多个 API 源,使用 Taotoken 聚合服务在运维复杂度上的降低

&#x1f680; 告别海外账号与网络限制&#xff01;稳定直连全球优质大模型&#xff0c;限时半价接入中。 &#x1f449; 点击领取海量免费额度 对比自行维护多个 API 源&#xff0c;使用 Taotoken 聚合服务在运维复杂度上的降低 在构建依赖多个大语言模型的应用时&#xff0c…...

GEO优化可以覆盖哪些搜索平台

这是一个非常现实的问题。企业投放资源做GEO&#xff0c;当然希望覆盖面越广越好。那么GEO优化到底能覆盖哪些平台&#xff1f;覆盖到什么程度&#xff1f;不同平台的GEO逻辑有什么差异&#xff1f;GEO平台覆盖的三个层级第一层级&#xff1a;通用大模型AI平台&#xff08;核心…...