状态模式——随遇而安

news/2024/4/27 1:54:13/文章来源:https://blog.csdn.net/qq_34368586/article/details/130379330

● 状态模式介绍

        状态模式中的行为是由状态来决定的,不用的状态下有不同的行为。状态模式和策略模式结构几乎完全一样,但它们的目的、本质却完全不一样就。状态模式的行为是平行的、不可替代的,策略模式的行为是彼此孤立、可相互替换的。用一句话来表述,状态模式把对象的行为包装在不同的状态对象里,每一个状态对象都有一个共同的抽象状态基类。状态模式的意图是让一个对象在其内部状态改变的时候,其行为也随之改变。

● 状态模式的定义

        当一个对象的内在状态改变时允许改变其行为,这个对象看起来像是改变了其类。

● 状态模式的使用场景

        (1)一个对象的行为取决于它的状态,并且它必须在运行时根据状态改变它的行为。

        (2)代码中包含大量与对象状态有关的条件语句,例如,一个操作中含有庞大的多分支语句(if-else或switch-case),且这些分支依赖于该对象的状态。

        状态模式将每一个条件分支放入一个独立的类中,这使得你可以根据对象自身的情况将对象的状态作为一个对象,这一对象可以不依赖于其他对象而独立变化,这样通过多态来去除过多的、重复的if-else等分支语句。

● 状态模式的UML类图

        UWL类图如下图所示。

        角色介绍,如何下图所示。

        ▶ Context:环境类,定义客户感兴趣的接口,维护一个State子类实例,这个实例定义了对象当前状态。

         ▶ State:抽象状态类或者状态接口,定义一个或者一组接口,表示该状态下的行为。

         ▶ ConereteStateA、ConereteStateB:具体状态类,每一个具体的状态类实现抽象State中定义的接口,从而达到不同的状态下的不同行为。

● 状态模式的简单示例

        下面我们就以电视遥控器为例来演示一下状态模式的实现。我们首先将电视的状态简单分为开机状态和关机状态,在开机状态下可以通过遥控器切换频道、调整音量等操作,但是,此时重复按开机键是无效的;而在关机状态下,频道切换、调整音量、关机都是无效的操作,只有按开机按钮时会生效。也就说电视的内部状态决定了遥控器的行为,我们看看第一版实现。

/*** 电视遥控器,含有开机、关机、下一频道、上一频道、调高音量、调低音量这几个功能*/
public class TvController {//开机状态private final static int POWER_ON = 1;//关机状态private final static int POWER_OFF = 2;private int mStata = POWER_OFF;public void powerOn() {mStata = POWER_ON;if (mStata == POWER_ON) {System.out.println("开机了");}}public void powerOff() {mStata = POWER_OFF;if (mStata == POWER_OFF) {System.out.println("关机了");}}public void nextChannel() {if (mStata == POWER_ON) {System.out.println("下一频道");} else {System.out.println("两个红灯提示没有开机");}}public void prevChannel() {if (mStata == POWER_ON) {System.out.println("上一频道");} else {System.out.println("两个红灯提示没有开机");}}public void turnUp() {if (mStata == POWER_ON) {System.out.println("调高音量");} else {System.out.println("两个红灯提示没有开机");}}public void turnDown() {if (mStata == POWER_ON) {System.out.println("调低音量");} else {System.out.println("两个红灯提示没有开机");}}
}

           可以看到,在TvController类中,通过mState字段存储了电视的状态,并且在各个操作中根据状态来判断是否应该执行。这就导致了在每个功能中都需求使用if-else,代码重复、相对较为混乱,这个是只有两状态和简单几个功能函数的情况下,那么当状态变成5个、功能函数变成10个呢?每个函数中都要用if-else进行判断,而这些代码都充斥在一个类中,这些重复的代码无法被提取出来,这使得这个类变得越来越难以维护。

        状态模式就是为了解决这类的问题而出现的,我们将这些状态用对象来替代,将这些行为封装到对象中,使得在不同的状态下有不同的实现,这样就将这些if-else从TvController类中去掉,整个结构也会变得清晰起来。我们看看实际代码。                            

/*** 电视状态接口,定义了电视操作的函数*/
public interface TvState {public void nestChannel();public void prevChannel();public void turnUp();public void turnDown();
}
/*** 关机状态,此时只开机状态是有效的*/
public class PowerOffState implements TvState{@Overridepublic void nestChannel() {}@Overridepublic void prevChannel() {}@Overridepublic void turnUp() {}@Overridepublic void turnDown() {}
}
/*** 开机状态,此时再触发开机功能不做任何操作*/
public class PowerOnState implements TvState{@Overridepublic void nestChannel() {System.out.println("下一频道");}@Overridepublic void prevChannel() {System.out.println("上一频道");}@Overridepublic void turnUp() {System.out.println("调高音量");}@Overridepublic void turnDown() {System.out.println("调低音量");}
}
/*** 电源操作接口*/
public interface PowerController {public void powerOn();public void powerOff();
}
/*** 电视遥控器,类似于经典模式中的Context*/
public class TvController implements PowerController{TvState mTvState;public void setTvState(TvState tvState){this.mTvState = tvState;}@Overridepublic void powerOn() {setTvState(new PowerOnState());System.out.println("开机了");}@Overridepublic void powerOff() {setTvState(new PowerOffState());System.out.println("关机了");}public void nextChannel(){mTvState.nestChannel();}public void prevChannel(){mTvState.prevChannel();}public void turnUp(){mTvState.turnUp();}public void turnDown(){mTvState.turnDown();}}
/*** 下面是客户端的代用代码*/
public class Client {public static void main(String[] args) {TvController tvController = new TvController();//设置开机状态tvController.powerOn();//下一频道tvController.nextChannel();//调高音量tvController.turnUp();//设置关键状态tvController.powerOff();//调高音量,此时不会生效tvController.turnUp();}
}

        输出解决如下:

开机了
下一频道
调高音量
关机了

        上述实现中,我们抽象了一个TvState接口,该接口中有操作电视的所有函数,该接口有连个实现类,即开机状态(PowerOnState)和关机状态(PowerOffState)。开机状态下只有开机功能是无效的,也就是说在已经开机的时候用户再按开机按钮不会产生任何反应;而在关机状态下,只有开机功能是可用的,其他功能都不会生效。同一个操作,如调高音量的turnUp函数,在关机状态下无效,在开机状态下就会将电视的音量调高,也就是电视的内部状态影响了电视遥控器的行为。状态模式将这些行为封装到状态类中,在进行操作的将这些功能转发给状态对象,不同的状态有不同的实现,这样就通过多态的形式去除了重复、杂乱的if-else语句,这这正是状态模式的精髓所在。

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

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

相关文章

微分方程数值解法(Runge-Kutta法PLC实现)

微分方程数值解法之欧拉法请参看下面的博客文章: 微分方程数值解法(PID仿真用一阶被控对象库PLC算法实现)_数学微积分算法plc编程实例_RXXW_Dor的博客-CSDN博客微分方程除极特殊情况外,大部分不可能求出它的精确解,只能用各种近似方法得到满足一定精度的近似解,微分方程由…

web端导航菜单系列

导航菜单属于导航中最常规的一种导航模式,它有2个显而易见的用途:帮助我们找到想要的任何东西和告诉我们现在身在何处。帮助用户在不同页面之间跳转找到目标功能。 导航作为网站或者平台的骨架,是产品设计中不容忽视的一环。结合自身对于导航…

如何建立Linux与git的连接?

文章目录 建立连接三板斧: 本文以Xshell为案例进行与git的连接! 建立连接三板斧: add , commit ,push Linux与git远程连接的方法: 1.设置全局的用户名和邮箱 git config – global user.name “你的用户名” git config – glo…

Springboot Mybatis使用pageHelper实现分页查询

以下介绍实战中数据库框架使用的是mybatis,对整合mybatis此处不做介绍。 使用pageHelper实现分页查询其实非常简单,共两步: 一、导入依赖; 二、添加配置; 那么开始, 第一步: pom.xml添加依…

不得不的创建型模式-原型模式

原型模式是一种创建型模式&#xff0c;它通过复制一个已有对象来创建新的对象&#xff0c;而无需知道新对象的具体类型。 原型模型的结构&#xff1a; 下面是一个简单的C实现原型模式的代码示例&#xff1a; #include <iostream> #include <string> #include <…

ChatGLM ptuning 的实战方案

大家好,我是herosunly。985院校硕士毕业,现担任算法研究员一职,热衷于机器学习算法研究与应用。曾获得阿里云天池比赛第一名,CCF比赛第二名,科大讯飞比赛第三名。拥有多项发明专利。对机器学习和深度学习拥有自己独到的见解。曾经辅导过若干个非计算机专业的学生进入到算法…

LVS+keepalived 群集

Keepalived及其工作原理 Keepalived 是一个基于VRRP协议来实现的LVS服务高可用方案&#xff0c;可以解决静态路由出现的单点故障问题 在一个LVS服务集群中通常有主服务器&#xff08;MASTER&#xff09;和备份服务器&#xff08;BACKUP&#xff09;两种角色的服务器&#xff…

tongweb

13051667606 东方通产品介绍 产品兼容 硬件要求 安装 安装目录结构 启动tongweb 停止tongweb bin下常用命令 企业版管理控制台 文档&#xff1a;产品简介及安装指南 绿色版直接解压安装 tar -zxvf …tar.gz Tongweb的配置文件 在conf的tongweb.xml 修改端口等信息 通过页面…

宁波博视眼科俞存院长:晒太阳会晒出白内障?是真的吗?

春意渐浓&#xff0c;人们纷纷踏出家门&#xff0c;享受暖暖的阳光。众所周知&#xff0c;适当晒太阳可以促进人体合成维生素D&#xff0c;对身体有一定的好处。 但你知道吗?太阳光中的紫外线可能会导致部分眼病的出现&#xff0c;例如&#xff1a;白内障。 晒太阳怎么会晒出白…

028:Mapbox GL 绘制线段,实时测量长度距离值

第028个 点击查看专栏目录 本示例的目的是介绍演示如何在vue+mapbox中添加draw组件,绘制线段,编辑线段,实时显示长度值。这里使用turf来计算长度值,采用默认的单位千米。 直接复制下面的 vue+mapbox源代码,操作2分钟即可运行实现效果 文章目录 示例效果配置方式示例源代…

使用WireShark抓包分析TCP_IP协议

文章目录 前言一、TCP/IP协议1.1 OSI分层1.2 TCP/IP 分层 二、抓包2.1 Socket代码2.2 过滤包 三、分析3.1 TCP首部3.2 实战分析3.3 三次握手3.4 四次挥手 参考 前言 TCP/IP 协议 是一组用于互联网通信的协议。它由两个主要协议组成&#xff1a;传输控制协议&#xff08;TCP&am…

RabbitMq-接收消息+redis消费者重复接收

在接触RammitMQ时&#xff0c;好多文章都说在配置中设置属性 # rabbitmq 配置 rabbitmq:host: xxx.xxx.xxx.xxxport: xxxxusername: xxxpassword: xxxxxx## 生产端配置# 开启发布确认,就是confirm模式. 消费端ack应答后,才将消息从队列中删除#确认消息已发送到队列(Queue)pub…

排查和解决CentOS系统上Nacos服务启动报错“java.net.UnknownHostException: jmenv.tbsite.net“问题

背景 环境是CentOS7操作系统&#xff0c;nacos服务宕掉了&#xff0c;启动服务的时候报错。 Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.boot.web.servlet.FilterRegistrationBean]: Factory method ‘di…

大数据管理中心规划设计方案(ppt可编辑)

本资料来源公开网络&#xff0c;仅供个人学习&#xff0c;请勿商用&#xff0c;如有侵权请联系删除。 统一汇聚 推动业务数据协同5 价值提炼 支撑精准服务与科学管理6 实时感知 辅助城市治理高效运行7 大数据资源平台目标体系规划11 建设目标与思路12 使能高效协同&#xff0…

Qt+MySql开发笔记:Qt5.9.3的msvc2017x64版本编译MySql8.0.16版本驱动并Demo连接数据库测试

若该文为原创文章&#xff0c;转载请注明原文出处 本文章博客地址&#xff1a;https://hpzwl.blog.csdn.net/article/details/130381428 红胖子网络科技博文大全&#xff1a;开发技术集合&#xff08;包含Qt实用技术、树莓派、三维、OpenCV、OpenGL、ffmpeg、OSG、单片机、软硬…

【数据结构初阶】第七节.树和二叉树的基本操作

作者简介&#xff1a;大家好&#xff0c;我是未央&#xff1b; 博客首页&#xff1a;未央.303 系列专栏&#xff1a;Java初阶数据结构 每日一句&#xff1a;人的一生&#xff0c;可以有所作为的时机只有一次&#xff0c;那就是现在&#xff01;&#xff01;&#xff01; 文章目…

港科夜闻|香港科技大学(广州)与中国电信广东公司签署战略合作协议

关注并星标 每周阅读港科夜闻 建立新视野 开启新思维 1、香港科技大学(广州)与中国电信广东公司签署战略合作协议。根据协议&#xff0c;双方将围绕各自科技创新及发展需要&#xff0c;整合双方的优质资源和优势能力&#xff0c;务实开展多方位的战略合作。本次合作&#xff0c…

6.微服务项目实战---Sleuth--链路追踪

6.1 链路追踪介绍 在大型系统的微服务化构建中&#xff0c;一个系统被拆分成了许多模块。这些模块负责不同的功能&#xff0c;组合成 系统&#xff0c;最终可以提供丰富的功能。在这种架构中&#xff0c;一次请求往往需要涉及到多个服务。互联网应用构建在不同的软件模块集上…

【算法与数据结构】6 学会对算法进行性能测试

欢迎来到爱书不爱输的程序猿的博客, 本博客致力于知识分享&#xff0c;与更多的人进行学习交流 本文收录于算法与数据结构体系专栏,本专栏对于0基础者极为友好,欢迎与我一起完成算法与数据结构的从0到1的跨越 算法性能测试 一、前情回顾二、算法性能测试1.生成测试用例2.使用测…

多种内网穿透的实现方案

1. 内网穿透的应用场景 1.1. 开发调试 比如企业微信、钉钉等开发&#xff0c;需要一个回调地址&#xff0c;开发的时候&#xff0c;希望回调到开发的电脑上&#xff0c;打断点进行调试&#xff0c;这就需要穿透到内网的开发机器。 1.2. 演示测试 有需要演示或测试的系统&am…