引言:在软件开发中,状态机(State Machine)是一种重要的概念,用于描述对象或系统在不同状态之间的转换和行为。理解状态机对于开发人员来说是至关重要的,特别是在处理复杂的业务逻辑或状态管理时。本文将深入探讨什么是状态机,以及它在软件开发中的应用。
题目
小黑子!面试官问我有用过状态机吗?
推荐解析
状态机的组成
状态机是一种抽象的数学模型,描述了对象或系统在特定时间点可能处于的各种状态以及状态之间的转换规则。它由一组状态、事件、转移和动作组成,用于模拟对象在不同条件下的行为和状态变化。
状态机包括以下基本组成部分:
状态(State):表示对象或系统当前的状态,例如开、关、就绪等。
事件(Event):触发状态转换的动作或条件,例如按钮点击、消息到达等。
转移(Transition):定义了从一个状态到另一个状态的转换规则,通常与特定事件相关联。
动作(Action):在状态转换过程中执行的操作或行为,例如更新状态、记录日志等。
状态机,也就是 State Machine ,不是指一台实际机器,而是指一个数学模型。说白了,一般就是指一张状态转换图。例如,根据自动门的运行规则,我们可以抽象出下面这么一个图。
简单实现
在计算机中,状态机通常用编程语言来实现。在 C、C++、Java、Python 等编程语言中,可以通过使用 switch-case 语句、if-else 语句、状态转移表等来实现状态机。在下面还有更加优雅的方式,使用 Spring 状态机 来实现。
if-else 实现状态机
在上面的示例中,我们使用
if-else
结构根据当前活动来控制音乐的播放状态,并执行相应的行为。代码如下:
public classBasketballMusicStateMachineUsingIfElse{
privateboolean isPlayingMusic;
publicBasketballMusicStateMachineUsingIfElse(){
this.isPlayingMusic = false; // 初始状态为音乐未播放
}
publicvoidplayMusic(){
if (!isPlayingMusic) {
System.out.println("Music starts playing...");
isPlayingMusic = true;
}
}
publicvoidstopMusic(){
if (isPlayingMusic) {
System.out.println("Music stops playing...");
isPlayingMusic = false;
}
}
publicvoidperformActivity(String activity){
if ("basketball".equals(activity)) {
System.out.println("Music~");
playMusic(); // 打篮球时播放音乐
} elseif ("sing_rap".equals(activity)) {
System.out.println("哎哟你干嘛!");
stopMusic(); // 唱跳Rap时停止音乐
} else {
System.out.println("Invalid activity!");
}
}
publicstaticvoidmain(String[] args){
BasketballMusicStateMachineUsingIfElse stateMachine = new BasketballMusicStateMachineUsingIfElse();
// 测试状态机
stateMachine.performActivity("basketball"); // 打篮球,音乐开始播放
stateMachine.performActivity("sing_rap"); // 唱跳Rap,音乐停止播放
stateMachine.performActivity("basketball"); // 再次打篮球,音乐重新开始播放
}
}
switch-case 实现状态机
在这个示例中,我们使用
switch-case
结构根据不同的活动来控制音乐的播放状态,并执行相应的行为。代码如下:
public classBasketballMusicStateMachineUsingSwitchCase{
privateboolean isPlayingMusic;
publicBasketballMusicStateMachineUsingSwitchCase(){
this.isPlayingMusic = false; // 初始状态为音乐未播放
}
publicvoidplayMusic(){
if (!isPlayingMusic) {
System.out.println("Music starts playing...");
isPlayingMusic = true;
}
}
publicvoidstopMusic(){
if (isPlayingMusic) {
System.out.println("Music stops playing...");
isPlayingMusic = false;
}
}
publicvoidperformActivity(String activity){
switch (activity) {
case"basketball":
System.out.println("Music ~");
playMusic(); // 打篮球时播放音乐
break;
case"sing_rap":
System.out.println("哎哟 你干嘛 ~");
stopMusic(); // 唱跳Rap时停止音乐
break;
default:
System.out.println("Invalid activity!");
}
}
publicstaticvoidmain(String[] args){
BasketballMusicStateMachineUsingSwitchCase stateMachine = new BasketballMusicStateMachineUsingSwitchCase();
// 测试状态机
stateMachine.performActivity("basketball"); // 打篮球,音乐开始播放
stateMachine.performActivity("sing_rap"); // 唱跳Rap,音乐停止播放
stateMachine.performActivity("basketball"); // 再次打篮球,音乐重新开始播放
}
}
是不是感觉状态机其实经常在我们的日常使用中捏~,接下来带大家使用更优雅的状态机 Spring 状态机。
使用 Spring 状态机
1)引入依赖
<dependency>
<groupId>org.springframework.statemachine</groupId>
<artifactId>spring-statemachine-core</artifactId>
<version>2.0.1.RELEASE</version>
</dependency>
2)定义状态和事件的枚举
代码如下:
publicenum States {
IDLE, // 空闲状态
PLAYING_BB, // 打篮球状态
SINGING // 唱跳Rap状态
}
publicenum Event {
START_BB_MUSIC, // 开始播放篮球音乐事件
STOP_BB_MUSIC // 停止篮球音乐事件
}
3)配置状态机
代码如下:
@Configuration
@EnableStateMachine
public classBasketballMusicStateMachineConfigextendsEnumStateMachineConfigurerAdapter<States, Event> {
@Autowired
private BasketballMusicStateMachineEventListener eventListener;
@Override
publicvoidconfigure(StateMachineConfigurationConfigurer<States, Event> config)throws Exception {
config
.withConfiguration()
.autoStartup(true)
.listener(eventListener); // 设置状态机事件监听器
}
@Override
publicvoidconfigure(StateMachineStateConfigurer<States, Event> states)throws Exception {
states
.withStates()
.initial(States.IDLE)
.states(EnumSet.allOf(States. class));
}
@Override
publicvoidconfigure(StateMachineTransitionConfigurer<States, Event> transitions)throws Exception {
transitions
.withExternal()
.source(States.IDLE).target(States.PLAYING_BB).event(Event.START_BB_MUSIC)
.and()
.withExternal()
.source(States.PLAYING_BB).target(States.SINGING).event(Event.STOP_BB_MUSIC)
.and()
.withExternal()
.source(States.SINGING).target(States.PLAYING_BB).event(Event.START_BB_MUSIC);
}
}
4)定义状态机事件监听器
代码如下:
@Component
public classBasketballMusicStateMachineEventListenerextendsStateMachineListenerAdapter<States, Event> {
@Override
publicvoidstateChanged(State<States, Event> from, State<States, Event> to){
if (from.getId() == States.IDLE && to.getId() == States.PLAYING_BB) {
System.out.println("开始打篮球,music 起");
} elseif (from.getId() == States.PLAYING_BB && to.getId() == States.SINGING) {
System.out.println("唱跳,你干嘛");
} elseif (from.getId() == States.SINGING && to.getId() == States.PLAYING_BB) {
System.out.println("继续打篮球,music 继续");
}
}
}
5)编写单元测试
@SpringBootTest
classChatApplicationTests{
@Resource
private StateMachine<States, Event> stateMachine;
@Test
voidcontextLoads(){
//开始打球,music 起
stateMachine.sendEvent(Event.START_BB_MUSIC);
//开始唱跳,你干嘛
stateMachine.sendEvent(Event.STOP_BB_MUSIC);
//继续打球,music 继续
stateMachine.sendEvent(Event.START_BB_MUSIC);
}
}
效果如下:
在上面的示例中,我们定义了一个状态机,用于控制在打篮球时音乐的播放和唱跳 Rap 的行为。通过触发事件来执行状态转移,并通过事件监听器监听状态变化并执行相应的操作。
推荐文章和书籍
文章:https://zhuanlan.zhihu.com/p/86293659
书籍:【 Java 核心技术卷 I 】
欢迎交流
当谈到状态机时,我们可以探讨以下几个问题:
1)什么是状态机?状态机在软件开发中有什么应用?
2)状态机的基本组成和工作原理是什么?
3)状态机和有限状态机(FSM)有何区别?
这些问题将帮助我们深入了解状态机的概念、应用和原理,从而更好地应用状态机解决实际问题。
点燃求职热情!每周持续更新,海量面试题和面经等你挑战!赶紧关注面试鸭公众号,轻松备战春招和暑期实习!
往期推荐