前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >java状态机的一种优雅写法

java状态机的一种优雅写法

原创
作者头像
haohulala
发布2023-03-27 21:53:53
8490
发布2023-03-27 21:53:53
举报
文章被收录于专栏:web应用编程web应用编程

状态机是一种数学模型,对于我们业务实现有很大的帮助。

我们可以用非常多的方法实现状态机,比如用茫茫多的if-else来进行条件判断,但是这种原始的方法并不够优雅,这篇文章就来介绍一个状态机优雅的实现方法。

话不多说,直接进入正题。

假设我们有两个状态

这个状态转换非常简单,我们来试着用java实现一下

程序结构如下图

我们先来介绍一下状态的表示

代码语言:javascript
复制
public class StateTransaction {

    // 当前状态
    private StateEnum currentState;
    // 相对应动作
    private ActionEnum action;
    // 下一个状态
    private StateEnum nextState;
    // 相应事件
    private Event event;

    public StateTransaction() {

    }

    public StateEnum getCurrentState() {
        return currentState;
    }

    public ActionEnum getAction() {
        return action;
    }

    public StateEnum getNextState() {
        return nextState;
    }

    public Event getEvent() {
        return event;
    }

    // 链式初始化对象
    public StateTransaction source(StateEnum state) {
        currentState = state;
        return this;
    }
    public StateTransaction when(ActionEnum action) {
        this.action = action;
        return this;
    }
    public StateTransaction target(StateEnum state) {
        nextState = state;
        return this;
    }
    public StateTransaction how(Event event) {
        this.event = event;
        return this;
    }

}

可以看到,表示状态的量一共有四个,分别是:

  • currentState:表示当前状态
  • action:表示相应动作
  • nextState:表示下一个状态
  • event:表示相应事件

这个四元组的含义就是,当处于currentState状态的时候,如果发生了action动作,就会转移到nextState状态,并且会触发event事件的响应。

注意看链式初始化的四个方法,这四个方法的定义让状态的初始化变得很优雅。

接着来看一下事件,Event是一个接口,其他具体的事件实现该接口进行某些操作,我们这个程序就直接打印一些信息,不做复杂的操作

代码语言:javascript
复制
public interface Event {

    public String handle();

}

public class PlayBasketballEvent implements Event{
    @Override
    public String handle() {
        System.out.println("开始打篮球");
        return "开始打篮球";
    }
}

public class SingDanceRapEvent implements Event{
    @Override
    public String handle() {
        System.out.println("开始唱,跳,rap");
        return "开始唱,跳,rap";
    }
}

除此之外,我们还要定义两个枚举类,分别表示状态和动作

代码语言:javascript
复制
public enum StateEnum {
    // 打篮球
    PLAY_BASKETBALL,
    // 唱跳rap
    SING_DANCE_RAP
}


public enum ActionEnum {
    // 音乐起
    MUSIC_ON,
    // 音乐结束
    MUSIC_OFF
}

上面准备工作都做完后,我们需要一个状态机类,来进行状态转移

代码语言:javascript
复制
public class StateMachine {

    // 存储状态信息
    private List<StateTransaction> stateTransactionList;
    // 记录当前状态
    private StateEnum currentState;

    public StateMachine(StateEnum state) {
        currentState = state;
        stateTransactionList = new ArrayList<>();
    }

    // 添加一条状态信息
    public StateTransaction addone() {
        StateTransaction stateTransaction = new StateTransaction();
        stateTransactionList.add(stateTransaction);
        return stateTransaction;
    }

    // 进行状态转移
    public StateTransaction execute(ActionEnum action) {
        for(int i=0; i<stateTransactionList.size(); i++) {
            if(currentState==stateTransactionList.get(i).getCurrentState() &&
                action==stateTransactionList.get(i).getAction()) {
                stateTransactionList.get(i).getEvent().handle();
                currentState = stateTransactionList.get(i).getNextState();
                return stateTransactionList.get(i);
            }
        }
        return null;
    }

}

上述代码有两个方法比较关键,分别是addone()和execute()

先来说addone(),方法首先初始化一个StateTransaction对象,然后放到List里面,最后将这个对象返回,我们拿到这个对象就可以往里面填内容了。

再说说execute(),这个方法接收ActionEnum作为参数,然后会遍历列表,寻找一条当前状态经过相应动作变化得到的下一个对象的这么一个状态信息,如果找到了就执行event中的handle()方法,并且将当前状态进行转移,最后将StateTransaction返回,如果没找到就返回null。

最后来看一下初始化的方法

代码语言:javascript
复制
public class StateMachineTest {

    public static void main(String[] args) {
        StateMachine machine = new StateMachine(StateEnum.PLAY_BASKETBALL);
        // 打篮球的时候,一旦音乐起,就会开始唱跳rap
        machine.addone().source(StateEnum.PLAY_BASKETBALL).when(ActionEnum.MUSIC_ON)
                .target(StateEnum.SING_DANCE_RAP).how(new SingDanceRapEvent());
        // 唱跳rap的时候,一旦音乐停止,就会开始打篮球
        machine.addone().source(StateEnum.SING_DANCE_RAP).when(ActionEnum.MUSIC_OFF)
                .target(StateEnum.PLAY_BASKETBALL).how(new PlayBasketballEvent());
        machine.execute(ActionEnum.MUSIC_ON);
        machine.execute(ActionEnum.MUSIC_OFF);
    }

}

可以看到,我们直接用链式的方法就能创建一条状态转移信息,非常优雅

程序输出如下

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
http://www.vxiaotou.com