设计模式之禅——状态模式

2024-09-04 11:32

本文主要是介绍设计模式之禅——状态模式,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

我们每个人都乘过电梯,电梯的动作:开门、关门、运行、停止。

现在我们用程序来实现一个电梯的动作,先看类图设计,如图所示

这里写图片描述

现在看一下代码

public interface ILift {//开启电梯public void open();//关闭电梯public void close();//能运行,能上能下public void run();//电梯还要能停下来ipublic void stop();
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
public class Lift implements ILift {@Overridepublic void open() {System.out.println("电梯门开启...");}@Overridepublic void close() {System.out.println("电梯门关闭...");}@Overridepublic void run() {System.out.println("电梯上下运行起来...");}@Overridepublic void stop() {System.out.println("电梯停止了...");}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
public class Client {public static void main(String[] args) {ILift lift = new Lift();//首先是电梯门打开,人进去lift.open();//r然后电梯门关闭lift.close();//电梯开始运行起来,向上或者向下lift.run();//最后到达目的地,电梯停下来lift.stop();}
}
/**Output
电梯门开启...
电梯门关闭...
电梯上下运行起来...
电梯停止了...
*/
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

代码非常简单,但是这个程序是有问题的。

———————————————————— 
1、电梯门可以打开,但不是随时都可以打开,电梯运行的时候不能突然开门。

2、电梯也不会出现停止了但是不开门的情况。 
————————————————————

所以我们可以看出,电梯的这四个动作的执行都有前置条件,具体点说就是在特定状态下才能做做特定的事。

接下来我们分析一下电梯有哪些特定的状态

  • 敞门状态 
    按了电梯上下按钮,电梯门开,这中间大概有10秒的时间,那就是敞门状态。在这个状态下电梯只能做的动作就是关门动作。

  • 闭门状态 
    电梯门关闭了,在这个状态下,可以进行的动作是:开门(我不想坐电梯了)、停止(忘记按路层号了)、运行。

  • 运行状态 
    电梯正在跑,上下窜,在这个状态下,电梯能做的是停止
  • 停止状态 
    电梯停止不动,在这个状态下,电梯有两个可选动作:继续运行和开门动作。

现在重新进行设计,类图如下: 
这里写图片描述

————————————————————————————— 
在接口中添加了标识变量,类LIft中的open、close等方法带有了逻辑判断。 
—————————————————————————————

代码如下:

public interface ILift {//电梯的4个状态final static int OPENING_STATE = 1;final static int CLOSING_STATE = 2;final static int RUNNING_STATE = 3;final static int STOPPING_STATE = 4;//开启电梯public void open();//关闭电梯public void close();//能运行,能上能下public void run();//电梯还要能停下来ipublic void stop();
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
public class Lift implements ILift {private int state;public void setState(int state) {this.state = state;}@Overridepublic void open() {//电梯在什么状态下才能开启switch (this.state) {case OPENING_STATE:break;case CLOSING_STATE:this.openWithoutLogic();this.setState(OPENING_STATE);break;case RUNNING_STATE:break;case STOPPING_STATE:this.openWithoutLogic();this.setState(OPENING_STATE);break;}}@Overridepublic void close() {//电梯在什么状态下才能关闭switch (this.state) {case OPENING_STATE:this.closeWithoutLogic();this.setState(CLOSING_STATE);break;case CLOSING_STATE:break;case RUNNING_STATE:break;case STOPPING_STATE:break;}}@Overridepublic void run() {switch (this.state) {case OPENING_STATE:break;case CLOSING_STATE:this.runWithoutLogic();this.setState(RUNNING_STATE);break;case RUNNING_STATE:break;case STOPPING_STATE:this.runWithoutLogic();this.setState(RUNNING_STATE);break;}}@Overridepublic void stop() {switch (this.state) {case OPENING_STATE:break;case CLOSING_STATE:this.stopWithoutLogic();this.setState(STOPPING_STATE);break;case RUNNING_STATE:this.runWithoutLogic();this.setState(STOPPING_STATE);break;case STOPPING_STATE:break;}}public void closeWithoutLogic(){System.out.println("电梯门关闭...");}public void openWithoutLogic(){System.out.println("电梯门开启...");}public void runWithoutLogic(){System.out.println("电梯上下运行起来...");}public void stopWithoutLogic(){System.out.println("电梯停止了...");}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
public class Client {public static void main(String[] args) {ILift lift = new Lift();lift.setState(OPENING_STATE);//首先是电梯门打开,人进去lift.open();//r然后电梯门关闭lift.close();//电梯开始运行起来,向上或者向下lift.run();//最后到达目的地,电梯停下来lift.stop();}
}
/**Output
电梯门开启...
电梯门关闭...
电梯上下运行起来...
电梯停止了...
*/
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

好了,改过的代码已经实现了简单的逻辑控制。但是它好像还是有不少问题。 
————————————————————————————

  • 电梯实现类Lift有点长 
    -长的原因是因为我们在程序中使用了大量的switch…case这样的判断(if…else也是一样)

  • 拓展类非常差,电梯还有两个状态,通电状态,断电状态,你要是在程序增加这两个方法,你看看Open()、Close()、Run()、Stop()这四个方法都要增加判断条件,也就是说在switch判断体中还要增加case项、这与开闭原则相违背。

  • 非常规状态无法实现 
    考虑电梯故障等情况就更麻烦了…

————————————————————————————— 
我们已经发现程序中有以上问题,我们怎么来修改呢? 
刚刚我们是从电梯的方法以及这些方法执行的条件去分析,现在我们换个角度来看问题。我们来想,电梯在具有这些状态的时候能够做什么实行,也就是说电梯在处于某个具体状态时,我们来思考 这个状态是由什么动作出发触发的而产生的,以及在这个状态下电梯还能做什么事情。例如,电器在停止状态时,我们来思考两个问题: 
————————————————————————————— 
- 停止状态是怎么来的,那当然是由于电梯执行了stop方法而来的。 

- 在停止状态下,电梯还能做什么动作?继续运行?开门?当然都可以了

————————————————————————————— 
我们再来分析其他三个状态,也都是一样的结果,我们只要实现电梯在一个状态下的两个任务模型就可以了:这个状态是如何产生的,以及在这个状态下还能做什么其他动作(也就是这个状态怎么过度到其他状态),既然我们以状态作为参考模型,那我们先定义电梯的状态接口,类图如下 
这里写图片描述

代码如下

public abstract class LiftState {//定义一个环境角色,也就是封装状态的变化引起的功能变化protected Context context = new 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();
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
public class Context {//定义出所有的电梯状态public final static OpenningState openningState =new OpenningState();public final static ClosingState closingState =new ClosingState();public final static RunningState runningState =new RunningState();public final static StoppingState stoppingState =new StoppingState();//定义一个当前的电梯状态private LiftState liftState;public LiftState getLiftState() {return liftState;}public void setLiftState(LiftState liftState) {this.liftState = liftState;// this.liftState.stop();}public void open() {this.liftState.open();}public void close() {this.liftState.close();}public void run() {this.liftState.run();}public void stop() {this.liftState.stop();}}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
public class ClosingState extends LiftState {//电梯门关闭,这是关闭状态要实现的动作@Overridepublic void close() {System.out.println("电梯门关闭...");}//电梯门关了再打开@Overridepublic void open() {super.context.setLiftState(Context.openningState);super.context.getLiftState().open();}@Overridepublic void run() {super.context.setLiftState(Context.runningState);super.context.getLiftState().run();}@Overridepublic void stop() {super.context.setLiftState(Context.stoppingState);super.context.getLiftState().stop();}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
public class OpenningState extends LiftState {@Overridepublic void open() {System.out.println("电梯门开启...");}@Overridepublic void close() {//状态修改super.context.setLiftState(Context.closingState);super.context.getLiftState().close();}@Overridepublic void run() {}@Overridepublic void stop() {}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
public class RunningState extends LiftState {@Overridepublic void open() {}@Overridepublic void close() {}@Overridepublic void run() {System.out.println("电梯门上下运行...");}@Overridepublic void stop() {super.context.setLiftState(Context.stoppingState);super.context.getLiftState().stop();}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
public class StoppingState extends LiftState {@Overridepublic void open() {super.context.setLiftState(Context.openningState);super.context.getLiftState().open();}@Overridepublic void close() {}@Overridepublic void run() {super.context.setLiftState(Context.runningState);super.context.getLiftState().run();}//电梯停止是怎么发生的@Overridepublic void stop() {System.out.println("电梯停止了...");}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

把逻辑放到没一个状态内,然后用context整合,做到添加状态的时候也不需要修改原来的代码。

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

状态模式的核心是封装,状态的变更引起了行为的变更,从外部看起来就好像这个对象对应的类发生了改变一样。

状态模式的角色主要有三个:抽象状态角色,具体状态 角色,环境角色

状态模式的优点:

  • 结构清晰,避免了过多的switch…case或者if…else语句的使用
  • 遵循设计原则,很多地体现了开闭原则和单一职责原则,每个状态都是一个子类。
  • 封装性比较好

    状态模式的缺点:

  • 类膨胀

状态模式的使用场景

  • 行为随状态改变而改变
  • 条件、分支判断语句的替代者

    注意事项

  • 子类最好不要超过5个

这篇关于设计模式之禅——状态模式的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1135898

相关文章

Redis Cluster模式配置

《RedisCluster模式配置》:本文主要介绍RedisCluster模式配置,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录分片 一、分片的本质与核心价值二、分片实现方案对比 ‌三、分片算法详解1. ‌范围分片(顺序分片)‌2. ‌哈希分片3. ‌虚

RabbitMQ工作模式中的RPC通信模式详解

《RabbitMQ工作模式中的RPC通信模式详解》在RabbitMQ中,RPC模式通过消息队列实现远程调用功能,这篇文章给大家介绍RabbitMQ工作模式之RPC通信模式,感兴趣的朋友一起看看吧... 目录RPC通信模式概述工作流程代码案例引入依赖常量类编写客户端代码编写服务端代码RPC通信模式概述在R

IIS 7.0 及更高版本中的 FTP 状态代码

《IIS7.0及更高版本中的FTP状态代码》本文介绍IIS7.0中的FTP状态代码,方便大家在使用iis中发现ftp的问题... 简介尝试使用 FTP 访问运行 Internet Information Services (IIS) 7.0 或更高版本的服务器上的内容时,IIS 将返回指示响应状态的数字代

SQL Server身份验证模式步骤和示例代码

《SQLServer身份验证模式步骤和示例代码》SQLServer是一个广泛使用的关系数据库管理系统,通常使用两种身份验证模式:Windows身份验证和SQLServer身份验证,本文将详细介绍身份... 目录身份验证方式的概念更改身份验证方式的步骤方法一:使用SQL Server Management S

Redis高可用-主从复制、哨兵模式与集群模式详解

《Redis高可用-主从复制、哨兵模式与集群模式详解》:本文主要介绍Redis高可用-主从复制、哨兵模式与集群模式的使用,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝... 目录Redis高可用-主从复制、哨兵模式与集群模式概要一、主从复制(Master-Slave Repli

一文带你搞懂Redis Stream的6种消息处理模式

《一文带你搞懂RedisStream的6种消息处理模式》Redis5.0版本引入的Stream数据类型,为Redis生态带来了强大而灵活的消息队列功能,本文将为大家详细介绍RedisStream的6... 目录1. 简单消费模式(Simple Consumption)基本概念核心命令实现示例使用场景优缺点2

使用Python实现IP地址和端口状态检测与监控

《使用Python实现IP地址和端口状态检测与监控》在网络运维和服务器管理中,IP地址和端口的可用性监控是保障业务连续性的基础需求,本文将带你用Python从零打造一个高可用IP监控系统,感兴趣的小伙... 目录概述:为什么需要IP监控系统使用步骤说明1. 环境准备2. 系统部署3. 核心功能配置系统效果展

Nginx location匹配模式与规则详解

《Nginxlocation匹配模式与规则详解》:本文主要介绍Nginxlocation匹配模式与规则,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、环境二、匹配模式1. 精准模式2. 前缀模式(不继续匹配正则)3. 前缀模式(继续匹配正则)4. 正则模式(大

SpringSecurity JWT基于令牌的无状态认证实现

《SpringSecurityJWT基于令牌的无状态认证实现》SpringSecurity中实现基于JWT的无状态认证是一种常见的做法,本文就来介绍一下SpringSecurityJWT基于令牌的无... 目录引言一、JWT基本原理与结构二、Spring Security JWT依赖配置三、JWT令牌生成与

关于WebSocket协议状态码解析

《关于WebSocket协议状态码解析》:本文主要介绍关于WebSocket协议状态码的使用方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录WebSocket协议状态码解析1. 引言2. WebSocket协议状态码概述3. WebSocket协议状态码详解3