当前位置: 首页 > 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…...

接口不是json的内容能用Jsonpath获取吗,如果不能,我们选用什么方法处理呢?

JsonPath 是一种专门用于查询和提取 JSON 数据的查询语言&#xff08;类似 XPath 用于 XML&#xff09;。以下是详细解答&#xff1a; ​JsonPath 的应用场景​ ​API 响应处理​&#xff1a;从 REST API 返回的 JSON 数据中提取特定字段。​配置文件解析​&#xff1a;读取 J…...

npm error Cannot read properties of null (reading ‘matches‘)

当在使用 npm 命令时遇到 Cannot read properties of null (reading matches) 错误&#xff0c;这通常表示代码尝试访问一个 null 对象的 matches 属性。以下是综合多个来源的解决策略&#xff0c;按优先级排列&#xff1a; 一、核心解决方法 1. 清理缓存与重新安装依赖&…...

网络安全厂商F5推出AI Gateway,化解大模型应用风险

AI正以前所未见的速度重塑数字化体验。然而&#xff0c;企业在加速落地现代化数字体验的过程中&#xff0c;其在保障和交付AI应用方面仍面临严峻挑战。这些应用需处理海量数据&#xff0c;涉及复杂流量模式&#xff0c;并引入更高级的安全威胁&#xff0c;而企业当前的安全能力…...

leetcode 2434. 使用机器人打印字典序最小的字符串 中等

给你一个字符串 s 和一个机器人&#xff0c;机器人当前有一个空字符串 t 。执行以下操作之一&#xff0c;直到 s 和 t 都变成空字符串&#xff1a; 删除字符串 s 的 第一个 字符&#xff0c;并将该字符给机器人。机器人把这个字符添加到 t 的尾部。删除字符串 t 的 最后一个 字…...

uniapp map组件的基础与实践

UniApp 中的 map 组件用于在应用中展示地图,并且支持在地图上添加标记、绘制线条和多边形等功能。以下是一些基本用法: 1. 基本结构 首先,确保你在页面的 .vue 文件中引入了 map 组件。以下是创建一个简单地图的基本代码结构: <template><view class="con…...

软件工程:如何做好软件产品

1、什么是产品 从项目到产品 产品&#xff1a;满足行业共性需求的标准产品。即要能够做到配置化的开发&#xff0c;用同一款产品最大限度地满足不同客户的需求&#xff0c;同时让产品具有可以快速响应客户需求变化的能力。 好的产品一定吸收了多个项目的共性&#xff0c;一定是…...

n皇后问题的 C++ 回溯算法教学攻略

一、问题描述 n皇后问题是经典的回溯算法问题。给定一个 nn 的棋盘&#xff0c;要求在棋盘上放置 n 个皇后&#xff0c;使得任何两个皇后之间不能互相攻击。皇后可以攻击同一行、同一列以及同一对角线上的棋子。我们需要找出所有的合法放置方案并输出方案数。 二、输入输出形…...

React源码阅读-fiber核心构建原理

React源码阅读(2)-fiber核心构建原理 好的&#xff0c;我明白了。您提供的文本主要介绍了 React 源码中 Fiber 核心的构建原理&#xff0c;涵盖了从执行上下文到构建、提交、调度等关键阶段&#xff0c;以及相关的代码实现。 您提出的关联问题也很重要&#xff0c;它们深入探讨…...

高效图像处理:使用 Pillow 进行格式转换与优化

高效图像处理:使用 Pillow 进行格式转换与优化 1. 背景引入 在图像处理应用中,格式转换、裁剪、压缩等操作是常见需求。Python 的 Pillow 库基于 PIL(Python Imaging Library),提供 轻量、强大 的图像处理能力,广泛用于 Web 开发、数据分析、机器学习 等领域。 本文将…...

Java Lambda 表达式的缺点和替代方案

Java 8 引入的 Lambda 表达式曾被誉为编写简洁、函数式代码的革命性工具。但说实话,它们并不是万能钥匙。它有不少问题,比如它没有宣传的那么易读,在某些场景下还带来性能开销。 作为一名多年与 Java 冗长语法搏斗的开发者,我找到了更注重清晰、可维护性和性能的替代方案。…...