状态🐔到底如何优雅的实现

状态机的组成

状态机是一种抽象的数学模型,描述了对象或系统在特定时间点可能处于的各种状态以及状态之间的转换规则。它由一组状态、事件、转移和动作组成,用于模拟对象在不同条件下的行为和状态变化。

状态机包括以下基本组成部分:

  • 状态(State):表示对象或系统当前的状态,例如开、关、就绪等。
  • 事件(Event):触发状态转换的动作或条件,例如按钮点击、消息到达等。
  • 转移(Transition):定义了从一个状态到另一个状态的转换规则,通常与特定事件相关联。
  • 动作(Action):在状态转换过程中执行的操作或行为,例如更新状态、记录日志等。

状态机,也就是 State Machine ,不是指一台实际机器,而是指一个数学模型。说白了,一般就是指一张状态转换图。例如,根据自动门的运行规则,我们可以抽象出下面这么一个图。

简单实现

在计算机中,状态机通常用编程语言来实现。在 C、C++、Java、Python 等编程语言中,可以通过使用 switch-case 语句、if-else 语句、状态转移表等来实现状态机。在下面还有更加优雅的方式,使用 Spring 状态机 来实现。

if-else 实现状态机

在上面的示例中,我们使用 if-else 结构根据当前活动来控制音乐的播放状态,并执行相应的行为。代码如下:

java 复制代码
public class BasketballMusicStateMachineUsingIfElse {
    private boolean isPlayingMusic;

    public BasketballMusicStateMachineUsingIfElse() {
        this.isPlayingMusic = false; // 初始状态为音乐未播放
    }

    public void playMusic() {
        if (!isPlayingMusic) {
            System.out.println("Music starts playing...");
            isPlayingMusic = true;
        }
    }

    public void stopMusic() {
        if (isPlayingMusic) {
            System.out.println("Music stops playing...");
            isPlayingMusic = false;
        }
    }

    public void performActivity(String activity) {
        if ("basketball".equals(activity)) {
            System.out.println("Music~");
            playMusic(); // 打篮球时播放音乐
        } else if ("sing_rap".equals(activity)) {
            System.out.println("哎哟你干嘛!");
            stopMusic(); // 唱跳Rap时停止音乐
        } else {
            System.out.println("Invalid activity!");
        }
    }

    public static void main(String[] args) {
        BasketballMusicStateMachineUsingIfElse stateMachine = new BasketballMusicStateMachineUsingIfElse();

        // 测试状态机
        stateMachine.performActivity("basketball"); // 打篮球,音乐开始播放
        stateMachine.performActivity("sing_rap"); // 唱跳Rap,音乐停止播放
        stateMachine.performActivity("basketball"); // 再次打篮球,音乐重新开始播放
    }
}

switch-case 实现状态机

在这个示例中,我们使用 switch-case 结构根据不同的活动来控制音乐的播放状态,并执行相应的行为。代码如下:

java 复制代码
public class BasketballMusicStateMachineUsingSwitchCase {
    private boolean isPlayingMusic;

    public BasketballMusicStateMachineUsingSwitchCase() {
        this.isPlayingMusic = false; // 初始状态为音乐未播放
    }

    public void playMusic() {
        if (!isPlayingMusic) {
            System.out.println("Music starts playing...");
            isPlayingMusic = true;
        }
    }

    public void stopMusic() {
        if (isPlayingMusic) {
            System.out.println("Music stops playing...");
            isPlayingMusic = false;
        }
    }

    public void performActivity(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!");
        }
    }

    public static void main(String[] args) {
        BasketballMusicStateMachineUsingSwitchCase stateMachine = new BasketballMusicStateMachineUsingSwitchCase();

        // 测试状态机
        stateMachine.performActivity("basketball"); // 打篮球,音乐开始播放
        stateMachine.performActivity("sing_rap"); // 唱跳Rap,音乐停止播放
        stateMachine.performActivity("basketball"); // 再次打篮球,音乐重新开始播放
    }
}

是不是感觉状态机其实经常在我们的日常使用中捏~,接下来带大家使用更优雅的状态机 Spring 状态机。

使用 Spring 状态机

1)引入依赖

xml 复制代码
<dependency>
    <groupId>org.springframework.statemachine</groupId>
    <artifactId>spring-statemachine-core</artifactId>
    <version>2.0.1.RELEASE</version>
</dependency>

2)定义状态和事件的枚举

代码如下:

java 复制代码
public enum States {
    IDLE,       // 空闲状态
    PLAYING_BB, // 打篮球状态
    SINGING     // 唱跳Rap状态
}
public enum Event {
    START_BB_MUSIC, // 开始播放篮球音乐事件
    STOP_BB_MUSIC   // 停止篮球音乐事件
}

3)配置状态机

代码如下:

java 复制代码
@Configuration
@EnableStateMachine
public class BasketballMusicStateMachineConfig extends EnumStateMachineConfigurerAdapter<States, Event> {

    @Autowired
    private BasketballMusicStateMachineEventListener eventListener;

    @Override
    public void configure(StateMachineConfigurationConfigurer<States, Event> config) throws Exception {
        config
            .withConfiguration()
            .autoStartup(true)
            .listener(eventListener); // 设置状态机事件监听器
    }

    @Override
    public void configure(StateMachineStateConfigurer<States, Event> states) throws Exception {
        states
            .withStates()
            .initial(States.IDLE)
            .states(EnumSet.allOf(States.class));
    }

    @Override
    public void configure(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)定义状态机事件监听器

代码如下:

java 复制代码
@Component
public class BasketballMusicStateMachineEventListener extends StateMachineListenerAdapter<States, Event> {

    @Override
    public void stateChanged(State<States, Event> from, State<States, Event> to) {
        if (from.getId() == States.IDLE && to.getId() == States.PLAYING_BB) {
            System.out.println("开始打篮球,music 起");
        } else if (from.getId() == States.PLAYING_BB && to.getId() == States.SINGING) {
            System.out.println("唱跳,你干嘛");
        } else if (from.getId() == States.SINGING && to.getId() == States.PLAYING_BB) {
            System.out.println("继续打篮球,music 继续");
        }
    }
}

5)编写单元测试

java 复制代码
@SpringBootTest
class ChatApplicationTests {
    @Resource
    private StateMachine<States, Event> stateMachine;

    @Test
    void contextLoads() {
        //开始打球,music 起
        stateMachine.sendEvent(Event.START_BB_MUSIC);
		//开始唱跳,你干嘛
        stateMachine.sendEvent(Event.STOP_BB_MUSIC);
		//继续打球,music 继续
        stateMachine.sendEvent(Event.START_BB_MUSIC);

    }
}

效果如下:

在上面的示例中,我们定义了一个状态机,用于控制在打篮球时音乐的播放和唱跳 Rap 的行为。通过触发事件来执行状态转移,并通过事件监听器监听状态变化并执行相应的操作。

相关推荐
独泪了无痕15 小时前
探索Pinyin4j-Java中的拼音转换利器
java·后端
码事漫谈15 小时前
从 Coze 居然要下线了说起
后端
想不到一个好的ID16 小时前
智能体开发
后端
皮皮林55116 小时前
为什么数据库连接池不采用 IO 多路复用?
后端
岁月宁静17 小时前
AI 应用开发工程师简历怎么写:HR 初筛、技术复筛、面试追问,三道关卡各看什么?
人工智能·python·面试
Dream it possible!19 小时前
LeetCode 面试经典 150_多维动态规划_交错字符串(97_C++_中等)
算法·leetcode·面试·动态规划
IT_陈寒20 小时前
Redis雪崩把我坑惨了,这是5年经验的教训
前端·人工智能·后端
量化分析码农20 小时前
【Python量化多因子实战 06】从因子到持仓:多因子选股实盘工程化落地
后端
贾公子20 小时前
检测链路的容量规划——用 Little's Law 算清 QPS、并发与积压
后端·python
kyson_20 小时前
Java 集合框架:从继承体系到数据结构,再到开发选型
后端