Java 设计模式——状态模式

目录

  • 1.概述
  • 2.结构
  • 3.案例实现
    • 3.1.抽象状态类
    • 3.2.具体状态类
    • 3.3.上下文类
    • 3.4.测试
  • 4.优缺点
  • 5.使用场景

1.概述

【例】通过按钮来控制一个电梯的状态,电梯有开门状态,关门状态,停止状态,运行状态。每一种状态改变,都有可能要根据其他状态来更新处理。例如,如果电梯门现在处于运行时状态,就不能进行开门操作,而如果电梯门是停止状态,就可以执行开门操作。其类图如下:
在这里插入图片描述
具体实现代码如下:

ILift.java

public interface ILift {//定义四个电梯状态的常量int OPENING_STATE = 1;int CLOSING_STATE = 2;int RUNNING_STATE = 3;int STOPPING_STATE = 4;//设置电梯状态的功能void setState(int state);//电梯操作功能void open();void close();void run();void stop();
}

Lift.java

//电梯类(ILift 接口的实现类)
public class Lift implements ILift {//声明一个记录当前电梯的状态private int state;public void setState(int state) {this.state = state;}public void open() {switch (state) { //当前电梯状态case OPENING_STATE ://什么事都不做break;case CLOSING_STATE :System.out.println("电梯打开了...");//设置当前电梯状态为开启状态setState(OPENING_STATE);break;case STOPPING_STATE :System.out.println("电梯打开了...");//设置当前电梯状态为开启状态setState(OPENING_STATE);break;case RUNNING_STATE ://什么事都不做break;}}public void close() {switch (this.state) {case OPENING_STATE:System.out.println("电梯关门了...");	//只有开门状态可以关闭电梯门,可以对应电梯状态表来看this.setState(CLOSING_STATE);		//关门之后电梯就是关闭状态了break;case CLOSING_STATE://do nothing 	//已经是关门状态,不能关门break;case RUNNING_STATE://do nothing 	//运行时电梯门是关着的,不能关门break;case STOPPING_STATE://do nothing 	//停止时电梯也是关着的,不能关门break;}}public void run() {switch (this.state) {case OPENING_STATE:	//电梯不能开着门就走//do nothingbreak;case CLOSING_STATE:	//门关了,可以运行了System.out.println("电梯开始运行了。。。");this.setState(RUNNING_STATE);//现在是运行状态break;case RUNNING_STATE://do nothing 已经是运行状态了break;case STOPPING_STATE:System.out.println("电梯开始运行了。。。");this.setState(RUNNING_STATE);break;}}public void stop() {switch (this.state) {case OPENING_STATE: 	//开门的电梯已经是是停止的了(正常情况下)//do nothingbreak;case CLOSING_STATE:		//关门时才可以停止System.out.println("电梯停止了。。。");this.setState(STOPPING_STATE);break;case RUNNING_STATE:		//运行时当然可以停止了System.out.println("电梯停止了。。。");this.setState(STOPPING_STATE);break;case STOPPING_STATE://do nothingbreak;}}
}

Client.java

public class Client {public static void main(String[] args) {//创建电梯对象Lift lift = new Lift();//设置当前电梯的状态lift.setState(ILift.OPENING_STATE);//打开lift.open();lift.close();lift.run();lift.stop();}
}

问题分析:

  • 使用了大量的 switch…case 这样的判断(换成 if…else 也是一样),使程序的可读性变差。
  • 扩展性很差,如果新加了断电的状态,则需要修改上面的判断逻辑。

(2)状态模式 (State Pattern) 是一种行为型设计模式,它允许在一个对象内部状态改变时改变它的行为。状态模式将状态封装成独立的类,并将动作委托给代表当前状态的对象,以此实现状态转换时的行为变化。使用状态模式,我们可以将状态转换规则封装在不同状态类中,让上下文对象从复杂的状态转换逻辑中解耦出来,同时利用多态特性让状态转换具有扩展性和灵活性。

2.结构

状态模式包含以下主要角色:

  • 上下文 (Context) 角色:是一个包含状态的对象,它在运行时根据不同的状态改变其行为。上下文对象持有一个对当前状态的引用,并将具体的操作委托给当前状态对象处理。
  • 抽象状态 (Abstract State) 角色:定义了一个公共的接口,用于封装不同状态的行为。可以是一个抽象类或接口,并声明了在不同状态下可能发生的方法。
  • 具体状态 (Concrete State) 角色:实现了抽象状态接口,并定义了在对应状态下具体的行为。每个具体状态类都封装了一种特定状态的行为。

3.案例实现

对上述电梯的案例使用状态模式进行改进。类图如下:
在这里插入图片描述
具体实现代码如下:

3.1.抽象状态类

LiftState.java()

//抽象状态类
public abstract class LiftState {//声明环境角色类变量protected Context context;public void setContext(Context context) {this.context = context;}//电梯开启操作public abstract void open();//电梯关闭操作public abstract void close();//电梯运行操作public abstract void run();//电梯停止操作public abstract void stop();
}

3.2.具体状态类

OpeningState.java

//电梯开启状态类
public class OpeningState extends LiftState {//当前状态要执行的方法public void open() {System.out.println("电梯开启。。。");}public void close() {//修改状态super.context.setLiftState(Context.CLOSING_STATE);//调用当前状态中的context中的close方法super.context.close();}public void run() {//什么都不做}public void stop() {//什么都不做}
}

ClosingState.java

//电梯关闭状态类
public class ClosingState extends LiftState {@Override//电梯门关闭,这是关闭状态要实现的动作public void close() {System.out.println("电梯门关闭...");}//电梯门关了再打开,逗你玩呢,那这个允许呀@Overridepublic void open() {super.context.setLiftState(Context.OPENING_STATE);super.context.open();}//电梯门关了就跑,这是再正常不过了@Overridepublic void run() {super.context.setLiftState(Context.RUNNING_STATE);super.context.run();}//电梯门关着,我就不按楼层@Overridepublic void stop() {super.context.setLiftState(Context.STOPPING_STATE);super.context.stop();}
}

RunningState.java

//电梯运行状态类
public class RunningState extends LiftState {//运行的时候开电梯门?你疯了!电梯不会给你开的@Overridepublic void open() {//do nothing}//电梯门关闭?这是肯定了@Overridepublic void close() {//虽然可以关门,但这个动作不归我执行//do nothing}//这是在运行状态下要实现的方法@Overridepublic void run() {System.out.println("电梯正在运行...");}//这个事绝对是合理的,光运行不停止还有谁敢做这个电梯?!估计只有上帝了@Overridepublic void stop() {super.context.setLiftState(Context.STOPPING_STATE);super.context.stop();}
}

StoppingState.java

//电梯停止状态类
public class StoppingState extends LiftState {//停止状态,开门,那是要的!@Overridepublic void open() {//状态修改super.context.setLiftState(Context.OPENING_STATE);//动作委托为CloseState来执行,也就是委托给了 ClosingState 子类执行这个动作super.context.getLiftState().open();}@Overridepublic void close() {//虽然可以关门,但这个动作不归我执行//状态修改super.context.setLiftState(Context.CLOSING_STATE);//动作委托为 CloseState 来执行,也就是委托给了 ClosingState 子类执行这个动作super.context.getLiftState().close();}//停止状态再跑起来,正常的很@Overridepublic void run() {//状态修改super.context.setLiftState(Context.RUNNING_STATE);//动作委托为 CloseState 来执行,也就是委托给了 ClosingState 子类执行这个动作super.context.getLiftState().run();}//停止状态是怎么发生的呢?当然是停止方法执行了@Overridepublic void stop() {System.out.println("电梯停止了...");}
}

3.3.上下文类

Context.java

//环境角色类
public class Context {//定义对应状态对象的常量public final static OpeningState OPENING_STATE = new OpeningState();public final static ClosingState CLOSING_STATE = new ClosingState();public final static RunningState RUNNING_STATE = new RunningState();public final static StoppingState STOPPING_STATE = new StoppingState();//定义一个当前电梯状态变量private LiftState liftState;public LiftState getLiftState() {return liftState;}//设置当前状态对象public void setLiftState(LiftState liftState) {this.liftState = liftState;//设置当前状态对象中的 Context 对象this.liftState.setContext(this);}public void open() {this.liftState.open();}public void close() {this.liftState.close();}public void run() {this.liftState.run();}public void stop() {this.liftState.stop();}
}

3.4.测试

Client.java

public class Client {public static void main(String[] args) {//创建环境角色对象Context context = new Context();//设置当前电梯装填context.setLiftState(new ClosingState());context.open();context.run();context.close();context.stop();}
}

4.优缺点

  • 优点:
    • 将状态封装成独立的类,使得状态转换的逻辑更加清晰,避免了大量的条件判断语句。
    • 将状态和状态切换的行为分离,使得状态转换的代码可扩展和可维护。增加新的状态类或修改现有的状态类时,对上下文对象和其他状态类都没有影响。
    • 增强了代码的可读性和可维护性。每个状态都有自己的类,使得代码具备了良好的结构化,并且易于理解和修改。
    • 通过引入多态性,可以方便地增加新的状态类,而不需要修改现有的代码。
  • 缺点:
    • 如果状态类很多,可能会导致类的数量庞大,增加了代码的复杂性。
    • 状态类的创建和销毁需要维护和管理,可能会增加系统的开销。
    • 当状态转换的逻辑较为简单时,使用状态模式可能会显得过于繁琐。

5.使用场景

(1)状态模式适用于以下情况:

  • 当一个对象的行为取决于其内部状态,并且在不同状态下需要有不同的行为时,可以使用状态模式。例如,订单状态的改变会导致不同的操作和行为,可以使用状态模式来管理订单的状态转换。
  • 当有大量的条件语句用于根据不同状态执行不同操作时,可以使用状态模式来简化代码并提高可维护性。
  • 当一个对象的行为需要根据多个条件进行判断,并且这些条件随着时间的推移可能会发生变化时,可以使用状态模式。通过将每个条件封装为一个状态类,可以方便地添加、修改或删除状态,而不会对其他部分产生影响。
  • 当需要在运行时动态改变对象的行为时,可以使用状态模式。通过改变对象的状态,可以改变其行为,而不需要修改对象的代码。

(2)总之,状态模式适用于需要根据对象的内部状态来改变其行为,并且希望将状态转换逻辑封装在独立的状态类中的情况。它提供了一种清晰、灵活和可扩展的方式来管理对象的状态和行为。

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

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

相关文章

GNU链接脚本详解

0. 前言 每一个链接都是由链接脚本控制的,链接脚本是用链接命令语言编写的脚本。链接都会用到一个链接脚本,如果你没有指定自己的脚本,就会使用默认的链接脚本。可以用 "--verbose" 命令行选项显示默认的连接脚本。指定命令行参数…

微服务之初始微服务

文章目录 一、服务架构演变1.单体架构2.分布式架构 二、认识微服务三、总结四、微服务技术对比五、SpringCloud注意 一、服务架构演变 1.单体架构 单体架构:将业务的所有功能集中在一个项目中开发,打成一个包部署。 优点: 架构简单部署成本…

G-LAB IT实验室【11月】网工公开课 即将开始~

带你一起走进网工的世界!G-LAB网工入门免费公开课即将开讲!无论是想学习基础网络组网还是网络互通技术实施,这个公开课都是你不容错过的! 公开课课程为期两天,11月7日&11月8日晚20:00 分享主题…

MFC-网络编程TCP服务端(NBlockSocket)

目录 1、NBlockSocket.h类: (1)、Init接口函数 (2)、Register接口函数 (3)、Send接口函数 (4)、Accept接口函数 (5)、Recv接口函数 2、实现…

分布式任务调度(00)--Quartz

1 任务调度整体流程 2 组件 调度器 :工厂类创建Scheduler,根据触发器定义的时间规则调度任务任务:Job表示被调度的任务触发器:Trigger 定义调度时间的元素,按啥时间规则执行任务。一个Job可被多个Trigger关联&#xf…

AI 绘画 | Stable Diffusion 图生图

图生图简介 Stable Diffusion 不仅可以文生图,还可以图生图。文生图就是完全用提示词文本去生成我们想要图片,但是很多时候会有词不达意的感觉。就像我们房子装修一样,我们只是通过文字描述很难表达出准确的想要的装修效果,如果能…

基于element-ui封装可配置表单组件

“vue”: “^2.7.13” “element-ui”: “^2.15.7” 代码地址 【说明】 该组件时使用vue3&#xff08;vue2.7&#xff09;语法封装&#xff0c;使用时可用vue2语法使用也可以使用vue3语法使用 一、vue2语法使用案例 基础用法 <template><div class"form-demo…

Freeswitch代码

1.引入依赖 Freeswitch依赖版本 <dependency><groupId>org.freeswitch.esl.client</groupId><artifactId>esl-client</artifactId><version>0.10.1</version> </dependency> 2.代码 import org.freeswitch.esl.client.inbound…

VR全景如何助力乡村振兴,乡村发展在哪些方面用到VR全景技术

引言&#xff1a; 乡村振兴是当今中国发展的重要战略&#xff0c;也是推动农村经济社会全面发展的关键举措。在这一过程中&#xff0c;虚拟现实&#xff08;VR&#xff09;全景技术正逐渐崭露头角&#xff0c;为乡村振兴提供了机遇。 一&#xff0e;VR全景技术的概念和应用 1…

[科研图像处理]用matlab平替image-j,有点麻烦,但很灵活!

做材料与生物相关方向的同学应该对image-j并不陌生&#xff0c;前几天有个师兄拜托我用image-j分析一些图片&#xff0c;但使用过后发现我由于不了解image-j的工作流程而对结果并不确信&#xff0c;而且image-j的功能无法拓展&#xff0c;对有些图片的处理效果并不好&#xff0…

在Google Kubernetes集群创建分布式Jenkins(一)

因为项目需要&#xff0c;在GKE的集群上需要创建一个CICD的环境&#xff0c;记录一下安装部署一个分布式Jenkins集群的过程。 分布式Jenkins由一个主服务器和多个Agent组成&#xff0c;Agent可以执行主服务器分派的任务。如下图所示&#xff1a; 如上图&#xff0c;Jenkins Ag…

ASO优化之为应用创建屏幕截图的技巧(下)

屏幕截图可以激发潜在用户对我们的应用程序的好奇心和兴趣。我们可以使用它们来吸引用户对产品页面的关注&#xff0c;解释应用程序&#xff0c;并强调其解决用户问题的能力。 1、美化屏幕截图背景。 为了使我们的屏幕截图更加有趣和美观&#xff0c;需要考虑使用背景。设计不…

【微服务】一体化智慧工地管理平台源码

智慧工地系统是一种利用人工智能和物联网技术来监测和管理建筑工地的系统。它可以通过感知设备、数据处理和分析、智能控制等技术手段&#xff0c;实现对工地施工、设备状态、人员安全等方面的实时监控和管理。 一、智慧工地让工程施工智能化 1、内容全面&#xff0c;多维度数…

Day1 ARM基础

【ARM课程认知】 1.ARM课程的作用 承上启下 基础授课阶段&#xff1a;c语言、数据结构、linux嵌入式应用层课程&#xff1a;IO、进程线程、网络编程嵌入式底层课程&#xff1a;ARM体系结构、系统移植、linux设备驱动c/QT 2.ARM课程需要掌握的内容 自己能够实现简单的汇编编…

登录Tomcat控制台,账号密码输入正确但点击登录没反应不跳转到控制台页面

在tomcat-users.xml里面可以查看登录tomcat控制台的账号密码&#xff0c;如果账号密码输入正确还是登录不进去&#xff0c;则很有可能是tomcat的账号被锁了&#xff08;可在catalina.xxx.log里面查看&#xff09;。tomcat账号被锁定后默认情况是不访问控制台后5分钟自动解锁&am…

持续集成交付CICD:安装Jenkins Slave(从节点)

目录 一、实验 1.安装Jenkins Slave&#xff08;从节点&#xff09; 二、问题 1.salve节点启动jenkins报错 2.终止命令行后jenkins从节点状态不在线 一、实验 1.安装Jenkins Slave&#xff08;从节点&#xff09; &#xff08;1&#xff09;查看jenkins版本 Version 2.…

Elasticsearch:搜索架构

Elasticsearch 全文检索的复杂性 为了理解为什么全文搜索是一个很难解决的问题&#xff0c;让我们想一个例子。 假设你正在托管一个博客发布网站&#xff0c;其中包含数亿甚至数十亿的博客文章&#xff0c;每个博客文章包含数百个单词&#xff0c;类似于 CSDN。 执行全文搜索…

11月7日 mybatis缓存

mybatis的缓存 缓存的条件&#xff1a;必须要有存在的数据 一级缓存: SqlSession级别缓存.存储的数据.只能在同一个SalSession有效.默认开启 二级缓存: SqlSessionFactory级别的缓存. SqlSessionFactory只有一个的,单例,全局共享的,不同的 SqlSession共享&#xff0c;默认没有…

基于Java+SpringBoot+Mybaties-plus+Vue+ElementUI 失物招领小程序 设计与实现

一.项目介绍 失物招领小程序 用户登录、忘记密码、退出系统 发布失物 和 发布招领 查看我发布的失物和招领信息 失捡物品模块可以查看和搜索所有用户发布的信息。 二.环境需要 1.运行环境&#xff1a;java jdk1.8 2.ide环境&#xff1a;IDEA、Eclipse、Myeclipse都可以&#…

Linux系统下一些配置建议整理

1. 【推荐】高并发服务器建议调小 TCP 协议的 time_wait 超时时间。 说明&#xff1a;操作系统默认 240 秒后&#xff0c;才会关闭处于 time_wait 状态的连接&#xff0c;在高并发访问下&#xff0c;服 务器端会因为处于 time_wait 的连接数太多&#xff0c;可能无法建立新的…