状态模式——对象状态及其转换

1、简介

1.1、概述

在软件系统中,有些对象也像水一样具有多种状态,这些状态在某些情况下能够相互转换,而且对象在不同的状态下也将具有不同的行为。为了更好地对这些具有多种状态的对象进行设计,可以使用一种被称为状态模式的设计模式。

状态模式用于解决系统中复杂对象的状态转换以及不同状态下行为的封装问题。当系统中某个对象存在多个状态,这些状态之间可以进行转换,而且对象在不同状态下行为不相同时可以使用状态模式。状态模式将一个对象的状态从该对象中分离出来,封装到专门的状态类中,使得对象状态可以灵活变化。对于客户端而言,无须关心对象状态的转换以及对象所处的当前状态,无论对于何种状态的对象,客户端都可以一致性地处理。

1.2、定义

状态模式(State Pattern):允许一个对象在其内部状态改变时改变它的行为,对象看起来似乎修改了它的类。其别名为状态对象(Objects for States),状态模式是一种对象行为型模式。

2、解析

2.1、UML类图

在状态模式中引入了抽象状态类和具体状态类,它们是状态模式的核心,其结构如下图所示。
在这里插入图片描述
可以看出,在状态模式结构图中包含以下3个角色:

  1. Context(环境类):环境类又称为上下文类,它是拥有多种状态的对象。由于环境类的状态存在多样性且在不同状态下对象的行为有所不同,因此将状态独立出去形成单独的状态类。在环境类中维护一个抽象状态类State的实例,这个实例定义当前状态,在具体实现时,它是一个State子类的对象。
  2. State(抽象状态类):它用于定义一个接口以封装与环境类的一个特定状态相关的行为。在抽象状态类中声明各种不同状态对应的方法,而在其子类中实现这些方法。由于不同状态下对象的行为可能不同,因此在不同子类中方法的实现可能存在不同,相同的方法可以写在抽象状态类中。
  3. 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(); // 调用状态对象的业务方法// 其他代码}
}

环境类实际上是真正拥有状态的对象,这里只是将环境类中与状态有关的代码提取出来封装到专门的状态类中。

在状态模式的使用过程中,一个对象的状态之间还可以进行相互转换。通常有以下两种实现状态转换的方式:

  1. 统一由环境类来负责状态之间的转换。此时,环境类还充当了状态管理器(State Manager)角色。在环境类的业务方法中通过对某些属性值的判断实现状态转换,还可以提供一个专门的方法用于实现属性判断和状态转换。代码片段如下:
public void changeState(){// 判断属性值,根据属性值进行状态转换if(value==0){this.setState(new ConcreteStateA());}else if(value==1){this.setState(new ConcreteStateB());}
}
  1. 由具体状态类来负责状态之间的转换。可以在具体状态类的业务方法中判断环境类的某些属性值再根据情况为环境类设置新的状态对象,实现状态转换。同样,也可以提供一个专门的方法来负责属性值的判断和状态转换。此时,状态类与环境类之间将存在依赖或关联关系,因为状态类需要访问环境类中的属性值。代码片段如下:
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、主要优点

  1. 封装了状态的转换规则。在状态模式中可以将状态的转换代码封装在环境类或者具体状态类中,对状态转换代码进行集中管理,而不是分散在一个个业务方法中。
  2. 将所有与某个状态有关的行为放到一个类中,只需要注入一个不同的状态对象即可使环境对象拥有不同的行为。
  3. 允许状态转换逻辑与状态对象合成一体,而不是提供一个巨大的条件语句块。状态模式可以避免使用庞大的条件语句来将业务方法和状态转换代码交织在一起。
  4. 可以让多个环境对象共享一个状态对象,从而减少系统中对象的个数。

3.2、主要缺点

  1. 状态模式的使用必然会增加系统中类和对象的个数,导致系统运行开销增大。
  2. 状态模式的程序结构与实现都较为复杂,如果使用不当将导致程序结构和代码的混乱,增加系统设计的难度。
  3. 状态模式对开闭原则的支持并不太好。增加新的状态类需要修改那些负责状态转换的源代码,否则无法转换到新增状态;而且修改某个状态类的行为也需修改对应类的源代码。

3.3、使用场景

  1. 对象的行为依赖于它的状态(例如某些属性值),状态的改变将导致行为的变化。
  2. 在代码中包含大量与对象状态有关的条件语句。这些条件语句的出现,会导致代码的可维护性和灵活性变差,不能方便地增加和删除状态,并且导致客户类与类库之间的耦合增强。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/82159.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

基于边缘无线协同感知的低功耗物联网LPIOT技术:赋能智慧园区方案以及数字工厂领域

回到2000年左右,物联网的底层技术支撑还是“ZigBee”,虽然当时ZigBee的终端功耗指标其实也并不庞大,但是,“拓扑复杂导致工程实施难度大”、“网络规模小导致的整体效率低下”都成为限制其发展的主要因素。 LPWAN,新一…

基于kettle实现pg数据定时转存mongodb

mogodb 待创建 基于kettle实现pg数据定时转存mongodb_kettle 实时迁移 mongodb_呆呆的私房菜的博客-CSDN博客

Redis安装以及配置隧道连接(centOs)

目录 1.centOs安装Redis 2. Redis 启动和停⽌ 3. 操作Redis 2.Xshell配置隧道 1.centOs安装Redis #使⽤yum安装Redis yum -y install redis 2. Redis 启动和停⽌ #查看是否启动 ps -ef|grep redis#启动redis: redis-server /etc/redis.conf &#停⽌Redis redis-cli sh…

项目出bug,找不到bug,如何拉回之前的版本

1.用gitee如何拉取代码 本文为转载于「闪耀太阳a」的原创文章原文链接:https://blog.csdn.net/Gufang617/article/details/119929145 怎么从gitee上拉取代码 1.首先找到gitee上想要拉取得代码URL地址 点击复制这里的https地址 1 ps:(另外一种方法&…

linux onlyOffice docker 离线部署

文章目录 前言1. 安装Docker容器2. 拉取镜像3. 验证 前言 docker 离线安装onlyoffice,如在线安装可直接跳过导出导入镜像步骤,拉取后直接运行。 1. 安装Docker容器 下载文件 wget https://download.docker.com/linux/static/stable/x86_64/docker-19…

x86 kgdb deug调试分析

本文主要是收集,以下文章写得很好,我二次整理一下。 如果要手动livedb. 1. call kdbg_arch_late() 2. kgd_set_hw_break(addr,8,1); 3. kgdb_correct_hw_break();// enable bp to cpu regs -------------------------------分割线----------------…

使用Automatic1111在本地PC上运行SDXL 1.0

这是我们部署Stable Diffusion的第三篇文章了,前两篇文章都详细介绍了Automatic1111的stable-diffusion-webui的安装,这次主要介绍如何使用SDXL 1.0模型。 获取项目 在本地获取Automatic1111’s WebUI项目,下载完成后应该是这个样子的 下载检…

(十二)大数据实战——hadoop集群之HDFS高可用自动故障转移

前言 本节内容主要介绍一下hadoop集群下实现HDFS高可用的自动故障转移,HDFS高可用的自动故障转移主要通过zookeeper实现故障的监控和主节点的切换。自动故障转移为 HDFS 部署增加了两个新组件:ZooKeeper 和 ZKFailoverController (ZKFC&…

Jmeter(六) - 从入门到精通 - 建立数据库测试计划(详解教程)

1.简介 在实际工作中,我们经常会听到数据库的性能和稳定性等等,这些有时候也需要测试工程师去评估和测试,因此这篇文章主要介绍了jmeter连接和创建数据库测试计划的过程,在文中通过示例和代码非常详细地介绍给大家,希望对各位小伙…

干货分享|Elsevier投稿进度查询功能正式上线,随时获取投稿状态!

想必广大科研学者们都经历过每天登录系统查看投稿进度的煎熬过程,为了方便广大科研人随时获取投稿状态,2023年8月,Elsevier【微信端投稿进度查询功能】正式上线! 无论你是通讯作者还是共同作者,只需一次查询&#xff…

实现vuex数据持久化处理

(创作不易,感谢有你,你的支持,就是我前行的最大动力,如果看完对你有帮助,请留下您的足迹) 目录 前言: 方案一 localStorage 介绍 值 示例 JSON.stringify() 介绍 语法 参数 返…

AVS3:跨多通道预测PMC

前面的文章中介绍了TSCPM,它是AVS3中用于intra模式的跨通道预测技术,它利用线性模型根据亮度重建像素预测色度像素, 跨通道预测技术用于去除不同通道间的冗余信息,TSCPM可以去除Y-Cb、Y-Cr通道间的冗余,然而却忽略了…

【css】组合器

组合器是解释选择器之间关系的某种机制。在简单选择器器之间,可以包含一个组合器,从而实现简单选择器难以达到的效果。 CSS 中有四种组合器: 后代选择器 (空格):匹配属于指定元素后代的所有元素,示例:div …

CTF流量题解http1.pcapng

使用Wireshark工具打开流量文件http1.pcapng,如下图所示。 在过滤检索栏输入http,wireshark自动进行过滤。 选中其中一条记录后,wireshark 下方显示若干信息。 Frame 81: 925 bytes on wire (7400 bits), 925 bytes captured (7400 bits) …

【设计模式——学习笔记】23种设计模式——中介者模式Observer(原理讲解+应用场景介绍+案例介绍+Java代码实现)

文章目录 案例引入案例一普通实现中介者模式 案例二 介绍基础介绍登场角色尚硅谷 《图解设计模式》 案例实现案例一:智能家庭类图实现 案例二:登录页面逻辑实现说明类图实现 总结文章说明 案例引入 案例一 普通实现 在租房过程中,客户可能…

【LeetCode每日一题】——219.存在重复元素II

文章目录 一【题目类别】二【题目难度】三【题目编号】四【题目描述】五【题目示例】六【题目提示】七【解题思路】八【时间频度】九【代码实现】十【提交结果】 一【题目类别】 哈希表 二【题目难度】 简单 三【题目编号】 219.存在重复元素II 四【题目描述】 给你一个…

mysql之存储过程

目录 一、mysql之存储过程的相关知识 1)存储过程的概念 2)存储过程的优点 二、存储过程的管理 1)创建存储过程 基本格式: 2)调用存储过程 格式: call 存储过程名称 3)查看存储过程 查…

在windows中使用parLapply函数执行并行计算

目录 1-lapply()函数介绍: 例子1: 例子2: 例子3: 2-在Windows使用并行计算,使用parLapply()函数 2.1-并行计算的准备阶段: 2.2-parLapply()函数介绍 2.3-使用parLapply()函数编写执行并行计算 2.4-…

Sentinel整合Spring Cloud Gateway、Zuul详解

Sentinel 支持对 Spring Cloud Gateway、Zuul 等主流的 API Gateway 进行限流。 Sentinel 1.6.0 引入了 Sentinel API Gateway Adapter Common 模块,此模块中包含网关限流的规则和自定义 API 的实体和管理逻辑: GatewayFlowRule:网关限流规则…

Python AI 绘画

Python AI 绘画 本文我们将为大家介绍如何基于一些开源的库来搭建一套自己的 AI 作图工具。 需要使用的开源库为 Stable Diffusion web UI,它是基于 Gradio 库的 Stable Diffusion 浏览器界面 Stable Diffusion web UI GitHub 地址:GitHub - AUTOMATI…