首页 > 文章列表 > 如何实现Java状态机?

如何实现Java状态机?

java
490 2023-05-10

Java状态机怎么实现

假设我们有两个状态

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

程序结构如下图

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

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是一个接口,其他具体的事件实现该接口进行某些操作,我们这个程序就直接打印一些信息,不做复杂的操作

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";

    }

}

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

public enum StateEnum {

    // 打篮球

    PLAY_BASKETBALL,

    // 唱跳rap

    SING_DANCE_RAP

}

public enum ActionEnum {

    // 音乐起

    MUSIC_ON,

    // 音乐结束

    MUSIC_OFF

}

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

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。

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

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);

    }

 

}

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

程序输出如下