当前位置: 首页 > news >正文

Gof23设计模式之状态模式

1.概述

【例】通过按钮来控制一个电梯的状态,一个电梯有开门状态,关门状态,停止状态,运行状态。每一种状态改变,都有可能要根据其他状态来更新处理。例如,如果电梯门现在处于运行时状态,就不能进行开门操作,而如果电梯门是停止状态,就可以执行开门操作。

public interface ILift {//电梯的4个状态//开门状态public final static int OPENING_STATE = 1;//关门状态public final static int CLOSING_STATE = 2;//运行状态public final static int RUNNING_STATE = 3;//停止状态public final static int STOPPING_STATE = 4;//设置电梯的状态public void setState(int state);//电梯的动作public void open();public void close();public void run();public void stop();
}public class Lift implements ILift {private int state;@Overridepublic void setState(int state) {this.state = state;}//执行关门动作@Overridepublic 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;}}//执行开门动作@Overridepublic void open() {switch (this.state) {case OPENING_STATE://门已经开了,不能再开门了//do nothingbreak;case CLOSING_STATE://关门状态,门打开:System.out.println("电梯门打开了。。。");this.setState(OPENING_STATE);break;case RUNNING_STATE://do nothing 运行时电梯不能开门break;case STOPPING_STATE:System.out.println("电梯门开了。。。");//电梯停了,可以开门了this.setState(OPENING_STATE);break;}}//执行运行动作@Overridepublic 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;}}//执行停止动作@Overridepublic 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;}}
}public class Client {public static void main(String[] args) {Lift lift = new Lift();lift.setState(ILift.STOPPING_STATE);//电梯是停止的lift.open();//开门lift.close();//关门lift.run();//运行lift.stop();//停止}
}

问题分析:

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

定义:

对有状态的对象,把复杂的“判断逻辑”提取到不同的状态对象中,允许状态对象在其内部状态发生改变时改变其行为。

2.结构

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

  • 环境(Context)角色:也称为上下文,它定义了客户程序需要的接口,维护一个当前状态,并将与状态相关的操作委托给当前状态对象来处理。
  • 抽象状态(State)角色:定义一个接口,用以封装环境对象中的特定状态所对应的行为。
  • 具体状态(Concrete State)角色:实现抽象状态所对应的行为。

3.案例实现

/*** @author 晓风残月Lx* @date 2023/7/29 14:57*      抽象状态类*/
public abstract class LiftState {// 声明环境角色类变量protected Context context;public void setContext(Context context) {this.context = context;}public Context getContext() {return context;}// 电梯开启操作public abstract void open();// 电梯关闭操作public abstract void close();// 电梯运行操作public abstract void run();// 电梯停止操作public abstract void stop();}

/*** @author 晓风残月Lx* @date 2023/7/29 15:00*      电梯开启状态类*/
public class OpeningState extends LiftState {//开启当然可以关闭了,我就想测试一下电梯门开关功能@Overridepublic void open() {System.out.println("电梯门开启...");}@Overridepublic void close() {//状态修改super.context.setLiftState(Context.CLOSING_STATE);//动作委托为CloseState来执行,也就是委托给了ClosingState子类执行这个动作super.context.getLiftState().close();}//电梯门不能开着就跑,这里什么也不做@Overridepublic void run() {//do nothing}//开门状态已经是停止的了@Overridepublic void stop() {//do nothing}
}

/*** @author 晓风残月Lx* @date 2023/7/29 15:00*      电梯运行状态类*/
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();}
}
/*** @author 晓风残月Lx* @date 2023/7/29 15:00*      电梯停止状态类*/
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("电梯停止了...");}
}
/*** @author 晓风残月Lx* @date 2023/7/29 15:00*      电梯关闭状态类*/
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();}
}
/*** @author 晓风残月Lx* @date 2023/7/29 14:58*      环境角色类*/
public class Context {// 定义对应状态对象的常量public final static OpeningState OPENING_STATE = new OpeningState();public final static RunningState RUNNING_STATE = new RunningState();public final static ClosingState CLOSING_STATE = new ClosingState();public final static StoppingState STOPPING_STATE = new StoppingState();// 定义一个当前电梯状态变量private LiftState liftState;// 设置当前状态对象public void setLiftState(LiftState liftState) {this.liftState = liftState;// 设置当前状态对象中的Context对象this.liftState.setContext(this);}public LiftState getLiftState() {return liftState;}public void open() {this.liftState.open();}public void close() {this.liftState.close();}public void stop() {this.liftState.stop();}public void run() {this.liftState.run();}}
//测试类
public class Client {public static void main(String[] args) {Context context = new Context();context.setLiftState(new RunningState());context.open();context.close();context.run();context.stop();}
}

4.优缺点

1.优点:

  • 将所有与某个状态有关的行为放到一个类中,并且可以方便地增加新的状态,只需要改变对象状态即可改变对象的行为。
  • 允许状态转换逻辑与状态对象合成一体,而不是某一个巨大的条件语句块。

2.缺点:

  • 状态模式的使用必然会增加系统类和对象的个数。
  • 状态模式的结构与实现都较为复杂,如果使用不当将导致程序结构和代码的混乱。
  • 状态模式对"开闭原则"的支持并不太好。

5.使用场景

  • 当一个对象的行为取决于它的状态,并且它必须在运行时根据状态改变它的行为时,就可以考虑使用状态模式。
  • 一个操作中含有庞大的分支结构,并且这些分支决定于对象的状态时。

相关文章:

Gof23设计模式之状态模式

1.概述 【例】通过按钮来控制一个电梯的状态,一个电梯有开门状态,关门状态,停止状态,运行状态。每一种状态改变,都有可能要根据其他状态来更新处理。例如,如果电梯门现在处于运行时状态,就不能…...

如何免费下载RunWayML产生的视频文件

问题: 首先没有下载的按钮。 其次如果直接“视频另存为”菜单,报错。 解决方案: 1)复制视频链接。 2)新开chrome,在url中粘贴上一步的url路径。 3)当看到视频后,在视频上面右键“…...

9.14 C++作业

仿照vector手动实现自己的myVector&#xff0c;最主要实现二倍扩容功能 #include <iostream>using namespace std;template <typename T> class Myvector {T *data; //存储数据的数组int len; //当前数组的长度int mycapa; //容纳数据的总容量public://…...

java关于文件记录篇章之文件夹创建篇

今天&#xff0c;创建一个文件夹目录的时候&#xff0c;创建多级目录的时候发现&#xff0c;自己老是创建失败&#xff0c;但是系统显示文件夹创建成功&#xff0c;但是你去找文件夹的时候&#xff0c;又发现创建失败&#xff0c;这里在我成功之后封装了一个创建文件夹的创建对…...

显示器显示的画面突然偏红色如何解决

显示器显示的画面突然偏红色如何解决 1. 概述2. 解决方法结束语 1. 概述 显示器显示的画面突然偏红色 &#xff0c;使用向日葵远程电脑&#xff0c;看到的画面是正常的&#xff0c;但是显示器上的画面确还是骗红的&#xff0c;这时候就需要看一下是不是开启了系统也夜间模式&a…...

【element-ui】 el-table 表格动态合并相同数据单元格最全教程,可指定列+自定义合并条件,附完整代码

el-table合并单元格 1.固定合并 官方挺提供的合并具体某行列的方法:el-table合并行或列通过给table传入span-method方法可以实现合并行或列,方法的参数是一个对象,里面包含当前行row、当前列column、当前行号rowIndex、当前列号columnIndex四个属性。 该函数可以返回一个包含…...

管理方法论:6. 正视团队冲突——化解危机,长治久安

概念 团队冲突指的是两个或两个以上的团队在目标、利益、认识等方面互不相容或互相排斥&#xff0c;从而产生心理或行为上的矛盾&#xff0c;导致抵触、争执或攻击事件。 参考&#xff1a; https://baike.baidu.com/item/%E5%9B%A2%E9%98%9F%E5%86%B2%E7%AA%81/6747073 htt…...

基于SpringBoot的一套强大后台管理系统

概述 一个功能强大而完善的后台管理系统框架&#xff0c;用户可基于此框架进行二次开发&#xff0c;定制成符合自己的需求的后台管理系统&#xff01; 详细 运行截图&#xff1a; 项目结构&#xff1a; 详细说明&#xff1a; 环境说明&#xff1a; jdk1.8mavenMySQL5.7 项…...

音乐项目后台管理系统出现的问题

1.当对歌手的歌曲进行编辑时候&#xff0c;会把所有的歌曲信息给修改了。 解决方法:修改controller层的中SongController代码中的这一行代码 boolean flag songService.updateById(song); 2.添加歌曲&#xff0c;在弹出框中输入&#xff0c;没有显示。原因&#xff1a;前端页…...

数据结构——图(图的存储及基本操作)

文章目录 前言一、邻接矩阵法&#xff08;顺序存储&#xff09;1.无向图存储邻接矩阵算法2.有向图存储邻接矩阵算法 二、邻接表法(图的链式存储结构)总结 前言 邻接矩阵法(图的顺序存储结构) 1.1 无向图邻接矩阵算法 1.2 有向图邻接矩阵算法邻接表法(图的一种链式存储结构) 一…...

2023年项目管理工具使用趋势分析及预测

随着技术的不断进步以及工作和领导态度的演变&#xff0c;各个行业都在经历着深刻的变革。项目管理领域同样如此&#xff0c;团队项目的技术和人员管理风格及策略正在不断地调整与优化&#xff0c;以适应新冠疫情后所呈现出的新的工作场所格局。在此背景下&#xff0c;以下是我…...

Vue3 实现一个无缝滚动组件(支持鼠标手动滚动)

Vue3 实现一个无缝滚动组件&#xff08;支持鼠标手动滚动&#xff09; 前言 在日常开发中&#xff0c;经常遇到需要支持列表循环滚动展示&#xff0c;特别是在数据化大屏开发中&#xff0c;无缝滚动使用频率更为频繁&#xff0c;在jquery时代&#xff0c;我们常用的无缝滚动组…...

【IP数据报】IP地址和MAC地址的区别

1、用IP地址来标识Internet的主机 在每个IP数据报中&#xff0c;都会携带源IP地址和目标IP地址来标识该IP数据报的源和目的主机。IP数据报在传输过程中&#xff0c;每个中间节点(IP 网关)还需要为其选择从源主机到目的主机的合适的转发路径(即路由)。IP协议可以根据路由选择协…...

高并发笔记

如何设计一个高并发系统&#xff1f;&#xff1a;https://mp.weixin.qq.com/s/yFc-70DEhloWn0G3GDa6Yw 分布式 ID 服务实践&#xff1a;https://mp.weixin.qq.com/s/KAts9Zjj8JpEd0Q6pqLlgQ 一文聊透布隆过滤器&#xff1a;https://mp.weixin.qq.com/s/qJ2fDm1Z57bPSzOBrgiqfg …...

eNSP网络学习

一、eNSP 1.什么是eNSP eNSP(Enterprise Network Simulation Platform)是一款由华为提供的免费的、可扩展的、图形化操作的网络仿真工具平台&#xff0c;主要对企业网络路由器、交换机进行软件仿真&#xff0c;完美呈现真实设备实景&#xff0c;支持大型网络模拟&#xff0c;让…...

广州xx策划公司MongoDB恢复-2023.09.09

2023.09.08用户的MongoDB数据库被勒索病毒攻击&#xff0c;数据全部被清空。 提示&#xff1a; mongoDB的默认端口为27017&#xff0c;黑客通常通过全网段扫描27017是否开放判断是否是MongoDB服务器。一旦发现27017开放&#xff0c;黑客就会用空密码、弱密码尝试连接数据库。黑…...

golang --- module-aware 模式下 包引入

一、文件列表如下 其中helloWorld目录是main包&#xff08;package&#xff09;所在目录&#xff0c;即该目录下所有的goy源文件&#xff08;不包含子目录&#xff09;属于main包&#xff0c;hello.go是mian函数所在文件 二、module-aware 模式启用 开启mod模式 go env -w G…...

从原理到实践 | Pytorch tensor 张量花式操作

文章目录 1.张量形状与维度1.1标量&#xff08;0维张量&#xff09;&#xff1a;1.2 向量&#xff08;1维张量&#xff09;&#xff1a;1.3矩阵&#xff08;2维张量&#xff09;&#xff1a;1.4高维张量&#xff1a; 2. 张量其他创建方式2.1 创建全零或全一张量&#xff1a;2.2…...

无涯教程-JavaScript - TRANSPOSE函数

描述 TRANSPOSE函数将单元格的垂直范围作为水平范围返回,反之亦然。必须将TRANSPOSE函数作为数组公式输入,该范围必须具有与行范围和列范围相同的行和列数。 您可以使用TRANSPOSE在工作表上移动数组或范围的垂直和水平方向。 语法 TRANSPOSE (array)键入函数后,按CTRL SHI…...

Webserver项目解析

一.webserver的组成部分 Buffer类 用于存储需要读写的数据 Channel类 存储文件描述符和相应的事件&#xff0c;当发生事件时&#xff0c;调用对应的回调函数 ChannelMap类 Channel数组&#xff0c;用于保存一系列的Channel Dispatcher 监听器&#xff0c;可以设置为epo…...

设计模式和设计原则回顾

设计模式和设计原则回顾 23种设计模式是设计原则的完美体现,设计原则设计原则是设计模式的理论基石, 设计模式 在经典的设计模式分类中(如《设计模式:可复用面向对象软件的基础》一书中),总共有23种设计模式,分为三大类: 一、创建型模式(5种) 1. 单例模式(Sing…...

【人工智能】神经网络的优化器optimizer(二):Adagrad自适应学习率优化器

一.自适应梯度算法Adagrad概述 Adagrad&#xff08;Adaptive Gradient Algorithm&#xff09;是一种自适应学习率的优化算法&#xff0c;由Duchi等人在2011年提出。其核心思想是针对不同参数自动调整学习率&#xff0c;适合处理稀疏数据和不同参数梯度差异较大的场景。Adagrad通…...

CentOS下的分布式内存计算Spark环境部署

一、Spark 核心架构与应用场景 1.1 分布式计算引擎的核心优势 Spark 是基于内存的分布式计算框架&#xff0c;相比 MapReduce 具有以下核心优势&#xff1a; 内存计算&#xff1a;数据可常驻内存&#xff0c;迭代计算性能提升 10-100 倍&#xff08;文档段落&#xff1a;3-79…...

PL0语法,分析器实现!

简介 PL/0 是一种简单的编程语言,通常用于教学编译原理。它的语法结构清晰,功能包括常量定义、变量声明、过程(子程序)定义以及基本的控制结构(如条件语句和循环语句)。 PL/0 语法规范 PL/0 是一种教学用的小型编程语言,由 Niklaus Wirth 设计,用于展示编译原理的核…...

爬虫基础学习day2

# 爬虫设计领域 工商&#xff1a;企查查、天眼查短视频&#xff1a;抖音、快手、西瓜 ---> 飞瓜电商&#xff1a;京东、淘宝、聚美优品、亚马逊 ---> 分析店铺经营决策标题、排名航空&#xff1a;抓取所有航空公司价格 ---> 去哪儿自媒体&#xff1a;采集自媒体数据进…...

算法岗面试经验分享-大模型篇

文章目录 A 基础语言模型A.1 TransformerA.2 Bert B 大语言模型结构B.1 GPTB.2 LLamaB.3 ChatGLMB.4 Qwen C 大语言模型微调C.1 Fine-tuningC.2 Adapter-tuningC.3 Prefix-tuningC.4 P-tuningC.5 LoRA A 基础语言模型 A.1 Transformer &#xff08;1&#xff09;资源 论文&a…...

Spring是如何解决Bean的循环依赖:三级缓存机制

1、什么是 Bean 的循环依赖 在 Spring框架中,Bean 的循环依赖是指多个 Bean 之间‌互相持有对方引用‌,形成闭环依赖关系的现象。 多个 Bean 的依赖关系构成环形链路,例如: 双向依赖:Bean A 依赖 Bean B,同时 Bean B 也依赖 Bean A(A↔B)。链条循环: Bean A → Bean…...

算法:模拟

1.替换所有的问号 1576. 替换所有的问号 - 力扣&#xff08;LeetCode&#xff09; ​遍历字符串​&#xff1a;通过外层循环逐一检查每个字符。​遇到 ? 时处理​&#xff1a; 内层循环遍历小写字母&#xff08;a 到 z&#xff09;。对每个字母检查是否满足&#xff1a; ​与…...

GitHub 趋势日报 (2025年06月06日)

&#x1f4ca; 由 TrendForge 系统生成 | &#x1f310; https://trendforge.devlive.org/ &#x1f310; 本日报中的项目描述已自动翻译为中文 &#x1f4c8; 今日获星趋势图 今日获星趋势图 590 cognee 551 onlook 399 project-based-learning 348 build-your-own-x 320 ne…...

探索Selenium:自动化测试的神奇钥匙

目录 一、Selenium 是什么1.1 定义与概念1.2 发展历程1.3 功能概述 二、Selenium 工作原理剖析2.1 架构组成2.2 工作流程2.3 通信机制 三、Selenium 的优势3.1 跨浏览器与平台支持3.2 丰富的语言支持3.3 强大的社区支持 四、Selenium 的应用场景4.1 Web 应用自动化测试4.2 数据…...