状态模式——对象状态及其转换
1、简介
1.1、概述
在软件系统中,有些对象也像水一样具有多种状态,这些状态在某些情况下能够相互转换,而且对象在不同的状态下也将具有不同的行为。为了更好地对这些具有多种状态的对象进行设计,可以使用一种被称为状态模式的设计模式。
状态模式用于解决系统中复杂对象的状态转换以及不同状态下行为的封装问题。当系统中某个对象存在多个状态,这些状态之间可以进行转换,而且对象在不同状态下行为不相同时可以使用状态模式。状态模式将一个对象的状态从该对象中分离出来,封装到专门的状态类中,使得对象状态可以灵活变化。对于客户端而言,无须关心对象状态的转换以及对象所处的当前状态,无论对于何种状态的对象,客户端都可以一致性地处理。
1.2、定义
状态模式(State Pattern):允许一个对象在其内部状态改变时改变它的行为,对象看起来似乎修改了它的类。其别名为状态对象(Objects for States),状态模式是一种对象行为型模式。
2、解析
2.1、UML类图
在状态模式中引入了抽象状态类和具体状态类,它们是状态模式的核心,其结构如下图所示。
可以看出,在状态模式结构图中包含以下3个角色:
- Context(环境类):环境类又称为上下文类,它是拥有多种状态的对象。由于环境类的状态存在多样性且在不同状态下对象的行为有所不同,因此将状态独立出去形成单独的状态类。在环境类中维护一个抽象状态类State的实例,这个实例定义当前状态,在具体实现时,它是一个State子类的对象。
- State(抽象状态类):它用于定义一个接口以封装与环境类的一个特定状态相关的行为。在抽象状态类中声明各种不同状态对应的方法,而在其子类中实现这些方法。由于不同状态下对象的行为可能不同,因此在不同子类中方法的实现可能存在不同,相同的方法可以写在抽象状态类中。
- ConcreteState(具体状态类):它是抽象状态类的子类,每个子类实现一个与环境类的一个状态相关的行为。每个具体状态类对应环境类的一个具体状态,不同的具体状态类其行为有所不同。
2.2、代码示例
在状态模式中,将对象在不同状态下的行为封装到不同的状态类中。为了让系统具有更好的灵活性和可扩展性,同时对各状态下的共有行为进行封装,需要对状态进行抽象,引入了抽象状态类角色,其典型代码如下:
/*** @Description: 抽象状态* @Author: yangyongbing* @CreateTime: 2023/08/03* @Version: 1.0*/
abstract class State {// 声明抽象业务方法,不同的具体状态类可以有不同的方法实现public abstract void handle();
}
在抽象状态类的子类即具体状态类中实现了在抽象状态类中声明的业务方法,不同的具体状态类可以提供完全不同的方法实现。在实际使用时,一个状态类中可能包含多个业务方法。如果在具体状态类中某些业务方法的实现完全相同,可以将这些方法移至抽象状态类,实现代码的复用。典型的具体状态类代码如下:
/*** @Description: 具体状态* @Author: yangyongbing* @CreateTime: 2023/08/03 19:45* @Version: 1.0*/
public class ConcreteState extends State{@Overridepublic void handle() {// 方法具体实现}
}
环境类维持一个对抽象状态类的引用。通过setState()方法可以向环境类注入不同的状态对象,再在环境类的业务方法中调用状态对象的方法,典型代码如下:
/*** @Description: 环境类* @Author: yangyongbing* @CreateTime: 2023/08/03 19:46* @Version: 1.0*/
public class Context {// 持有一个对抽象状态对象的引用private State state;// 其它属性值,该属性值的变化可能会导致对象状态发生变化private int value;// 设置状态对象public void setState(State state) {this.state = state;}public void request(){// 其它代码state.handle(); // 调用状态对象的业务方法// 其他代码}
}
环境类实际上是真正拥有状态的对象,这里只是将环境类中与状态有关的代码提取出来封装到专门的状态类中。
在状态模式的使用过程中,一个对象的状态之间还可以进行相互转换。通常有以下两种实现状态转换的方式:
- 统一由环境类来负责状态之间的转换。此时,环境类还充当了状态管理器(State Manager)角色。在环境类的业务方法中通过对某些属性值的判断实现状态转换,还可以提供一个专门的方法用于实现属性判断和状态转换。代码片段如下:
public void changeState(){// 判断属性值,根据属性值进行状态转换if(value==0){this.setState(new ConcreteStateA());}else if(value==1){this.setState(new ConcreteStateB());}
}
- 由具体状态类来负责状态之间的转换。可以在具体状态类的业务方法中判断环境类的某些属性值再根据情况为环境类设置新的状态对象,实现状态转换。同样,也可以提供一个专门的方法来负责属性值的判断和状态转换。此时,状态类与环境类之间将存在依赖或关联关系,因为状态类需要访问环境类中的属性值。代码片段如下:
public void changeState(Context ctx){// 判断属性值,根据属性值进行状态转换if(ctx.getValue()==1){this.setState(new ConcreteStateB());}else if(ctx.getValue()==2){this.setState(new ConcreteStateC());}
}
2.3、共享状态
在有些情况下,多个环境对象可能需要共享同一个状态。如果希望在系统中实现多个环境对象共享一个或多个状态对象,那么需要将这些状态对象定义为环境类的静态成员对象。
下面通过一个简单实例来说明如何实现共享状态。如果某系统要求两个开关对象要么都处于开的状态,要么都处于关的状态,在使用时它们的状态必须保持一致。开关可以由开转换到关,也可以由关转换到开。
可以使用状态模式来实现开关的设计,其结构如下图所示:
3、状态模式总结
状态模式将一个对象在不同状态下的不同行为封装在一个个状态类中。通过设置不同的状态对象可以让环境对象拥有不同的行为,而状态转换的细节对于客户端而言是透明的,方便了客户端的使用。在实际开发中,状态模式具有较高的使用频率,在工作流、游戏等软件中状态模式都得到了广泛应用,例如公文状态的转换、游戏中角色的升级等。
3.1、主要优点
- 封装了状态的转换规则。在状态模式中可以将状态的转换代码封装在环境类或者具体状态类中,对状态转换代码进行集中管理,而不是分散在一个个业务方法中。
- 将所有与某个状态有关的行为放到一个类中,只需要注入一个不同的状态对象即可使环境对象拥有不同的行为。
- 允许状态转换逻辑与状态对象合成一体,而不是提供一个巨大的条件语句块。状态模式可以避免使用庞大的条件语句来将业务方法和状态转换代码交织在一起。
- 可以让多个环境对象共享一个状态对象,从而减少系统中对象的个数。
3.2、主要缺点
- 状态模式的使用必然会增加系统中类和对象的个数,导致系统运行开销增大。
- 状态模式的程序结构与实现都较为复杂,如果使用不当将导致程序结构和代码的混乱,增加系统设计的难度。
- 状态模式对开闭原则的支持并不太好。增加新的状态类需要修改那些负责状态转换的源代码,否则无法转换到新增状态;而且修改某个状态类的行为也需修改对应类的源代码。
3.3、使用场景
- 对象的行为依赖于它的状态(例如某些属性值),状态的改变将导致行为的变化。
- 在代码中包含大量与对象状态有关的条件语句。这些条件语句的出现,会导致代码的可维护性和灵活性变差,不能方便地增加和删除状态,并且导致客户类与类库之间的耦合增强。
相关文章:

状态模式——对象状态及其转换
1、简介 1.1、概述 在软件系统中,有些对象也像水一样具有多种状态,这些状态在某些情况下能够相互转换,而且对象在不同的状态下也将具有不同的行为。为了更好地对这些具有多种状态的对象进行设计,可以使用一种被称为状态模式的设…...
Linux一阶段复习
Linux之父是林纳斯本纳第克特托瓦兹 Apache发布目录:/var/www/html nginx发布目录:/usr/share/nginx/html/ 配置dns的文件 : /etc/resolv.conf nginx的配置文件:/etc/nginx/ yum源的配置文件:/etc/yum.repos.d/ …...
宝塔Linux面板怎么升级?升级命令及失败解决方法
宝塔Linux面板怎么升级到新版本?root账号ssh登录到云服务器后,执行宝塔Linux面板升级命令即可搞定,新手站长分享宝塔Linux面板升级命令: 宝塔面板升级到新版本 1、使用root账号ssh登录到云服务器上 ssh root你的云服务器ip地址…...
前端面试的性能优化部分(6)每天10个小知识点
目录 系列文章目录前端面试的性能优化部分(1)每天10个小知识点前端面试的性能优化部分(2)每天10个小知识点前端面试的性能优化部分(3)每天10个小知识点前端面试的性能优化部分(4)每天…...

2023年 Java 面试八股文(20w字)
目录 第一章-Java基础篇 1、你是怎样理解OOP面向对象 难度系数:⭐ 2、重载与重写区别 难度系数:⭐ 3、接口与抽象类的区别 难度系数:⭐ 4、深拷贝与浅拷贝的理解 难度系数:⭐ 5、sleep和wait区别 难度系数&a…...
银河麒麟服务器ky10-server在线一键安装docker
脚本代码 # ---------------在线安装docker------------------- yum install docker -y # 修改docker拉取源为国内 rm -rf /etc/docker mkdir -p /etc/docker touch /etc/docker/daemon.json cat >/etc/docker/daemon.json<<EOF{"registry-mirrors": [&q…...

spring boot中web容器配置
web容器配置 spring boot 默认的web容器是 tomcat,如果需要换成其他的 web 容器,可以如下配置。 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId><!-- 默…...

DNSlog注入(利用DNSlog平台将SQL盲注变成回显注入)
前言什么是UNC什么是DNSlog注入DNSlog注入的条件防止DNSlog注入的几个措施 sqli-labs试验 前言 前几天面试的时候,面试官问我知不知道OOB(带外数据)。 当时我蒙了,确实没听说过这个东西,然后面试官告诉我原来dnslog注入…...

vim学习笔记(致敬vim作者)
vim cheat sheet 30. vim 删除大法 vim 删除某个字符之后改行的其他的字符?删除某行之后的其他行?删除某个字符之后的其他字符?【1】删除单个字符? 跳到要删除的字符位置 按下d键然后按下shift 4键 【2】删除某行之后的其他行…...

力扣 -- 139. 单词拆分
一、题目 题目链接:139. 单词拆分 - 力扣(LeetCode) 二、解题步骤 下面是用动态规划的思想解决这道题的过程,相信各位小伙伴都能看懂并且掌握这道经典的动规题目滴。 三、参考代码 class Solution { public:bool wordBreak(str…...
百度秋招攻略,百度网申笔试面试详解
百度秋招简介 作为行业巨头,百度向社会提供的岗位一直都是非常吃香的,每年也都有很多考生密切关注,百度发布的招聘广告,以尽可能的让自己进入这家企业工作,实现自己的人生价值。那么百度每年的秋招时间是多久…...
nohup Java -jar 生成的nohup.out 文件一直增加,如何处理
目录 1 实现 1 实现 除了使用echo "" > filename清空文件内容之外,还有其他几种方法可以删除文件中的内容而不删除文件本身:使用truncate命令:truncate命令可以用来截断文件并清空内容。使用以下命令清空文件内容:t…...

静态页面与动态页面的区别及部署jpress应用
简述静态网页和动态网页的区别 静态网页: 1、首先是静态网页,静态网页每个网页中都有一个固定的URL,网页URL以htm、HTML、jpg、.gif、.mp4等常见形式为后缀,而且不含有问号; 2、静态网页内容一经发布到网页服务器上…...

华为数通HCIA-华为VRP系统基础
什么是VRP? VRP是华为公司数据通信产品的通用操作系统平台,作为华为公司从低端到核心的全系列路由器、以太网交换机、业务网关等产品的软件核心引擎。 VRP提供以下功能: 实现统一的用户界面和管理界面 实现控制平面功能,并定义转发平面接口…...

基于Azure OpenAI Service 的知识库搭建实验⼿册
1.概要 介绍如何使⽤Azure OpenAI Service 的嵌⼊技术,创建知识库;以及创建必要的资源组和资源,包括 Form Recognizer 资源和 Azure 翻译器资源。在创建问答机器⼈服务时,需要使⽤已部署模型的 Azure OpenAI 资源、已存在的…...
第七节--结构体
lesson19: 一、结构的基础知识 1.数组是一组xx集合(1:10:10) 2.结构体是XX的集合(1:10:25) 二、结构的声明(看课件)(1:12…...

Docker学习(二十四)报错速查手册
目录 一、This error may indicate that the docker daemon is not running 报错docker login 报错截图:原因分析:解决方案: 二、Get "https://harbor.xxx.cn/v2/": EOF 报错docker login 报错截图:原因分析:…...
一种SpringBoot下Scheduler定时任务优雅退出方案
背景 近期业务中有一个定时任务发现每次服务部署时,偶发性的会触发问题,这里记录一下问题的跟进解决。 分析现象 该定时任务每2分钟执行一次,完成数据的更新处理。同时服务部署了多个服务器节点,为保证每次只有一个服务器节点上…...

DNS部署与安全详解(上)
文章目录 一、DNS二、域名组成1. 域名组成概述2. 域名组成 三、监听端口四、DNS解析种类1. 按照查询方式分类:2. 按照查询内容分类: 五、DNS服务器搭建过程1. 先确保服务器的IP地址是固定的2. 安装DNS软件 一、DNS DNS全称Domain Name Service࿰…...

【51单片机】晨启科技,酷黑版,音乐播放器
四、音乐播放器 任务要求: 设计制作一个简易音乐播放器(通过手柄板上的蜂鸣器发声,播放2到4首音乐),同时LED模块闪烁,给人视、听觉美的感受。 评分细则: 按下播放按键A6开始播放音乐࿰…...

wordpress后台更新后 前端没变化的解决方法
使用siteground主机的wordpress网站,会出现更新了网站内容和修改了php模板文件、js文件、css文件、图片文件后,网站没有变化的情况。 不熟悉siteground主机的新手,遇到这个问题,就很抓狂,明明是哪都没操作错误&#x…...
挑战杯推荐项目
“人工智能”创意赛 - 智能艺术创作助手:借助大模型技术,开发能根据用户输入的主题、风格等要求,生成绘画、音乐、文学作品等多种形式艺术创作灵感或初稿的应用,帮助艺术家和创意爱好者激发创意、提高创作效率。 - 个性化梦境…...

深入剖析AI大模型:大模型时代的 Prompt 工程全解析
今天聊的内容,我认为是AI开发里面非常重要的内容。它在AI开发里无处不在,当你对 AI 助手说 "用李白的风格写一首关于人工智能的诗",或者让翻译模型 "将这段合同翻译成商务日语" 时,输入的这句话就是 Prompt。…...
【算法训练营Day07】字符串part1
文章目录 反转字符串反转字符串II替换数字 反转字符串 题目链接:344. 反转字符串 双指针法,两个指针的元素直接调转即可 class Solution {public void reverseString(char[] s) {int head 0;int end s.length - 1;while(head < end) {char temp …...

从零实现STL哈希容器:unordered_map/unordered_set封装详解
本篇文章是对C学习的STL哈希容器自主实现部分的学习分享 希望也能为你带来些帮助~ 那咱们废话不多说,直接开始吧! 一、源码结构分析 1. SGISTL30实现剖析 // hash_set核心结构 template <class Value, class HashFcn, ...> class hash_set {ty…...

EtherNet/IP转DeviceNet协议网关详解
一,设备主要功能 疆鸿智能JH-DVN-EIP本产品是自主研发的一款EtherNet/IP从站功能的通讯网关。该产品主要功能是连接DeviceNet总线和EtherNet/IP网络,本网关连接到EtherNet/IP总线中做为从站使用,连接到DeviceNet总线中做为从站使用。 在自动…...

【OSG学习笔记】Day 16: 骨骼动画与蒙皮(osgAnimation)
骨骼动画基础 骨骼动画是 3D 计算机图形中常用的技术,它通过以下两个主要组件实现角色动画。 骨骼系统 (Skeleton):由层级结构的骨头组成,类似于人体骨骼蒙皮 (Mesh Skinning):将模型网格顶点绑定到骨骼上,使骨骼移动…...

深度学习习题2
1.如果增加神经网络的宽度,精确度会增加到一个特定阈值后,便开始降低。造成这一现象的可能原因是什么? A、即使增加卷积核的数量,只有少部分的核会被用作预测 B、当卷积核数量增加时,神经网络的预测能力会降低 C、当卷…...
在Ubuntu24上采用Wine打开SourceInsight
1. 安装wine sudo apt install wine 2. 安装32位库支持,SourceInsight是32位程序 sudo dpkg --add-architecture i386 sudo apt update sudo apt install wine32:i386 3. 验证安装 wine --version 4. 安装必要的字体和库(解决显示问题) sudo apt install fonts-wqy…...

Mysql中select查询语句的执行过程
目录 1、介绍 1.1、组件介绍 1.2、Sql执行顺序 2、执行流程 2.1. 连接与认证 2.2. 查询缓存 2.3. 语法解析(Parser) 2.4、执行sql 1. 预处理(Preprocessor) 2. 查询优化器(Optimizer) 3. 执行器…...