参考文档:状态机的两种实现模式
理论上if-else可以解决所有的业务逻辑,但当规则继续增加复杂度,或者规则总是变化,需要增加扩展性时,状态机是解决方案之一
因为状态机只是一种思想,具体的实现方式有很多,对于我而言,最容易理解的就是从图的角度进行理解和实现
一、从图中抽象出对象
从图中可以抽象出以下对象
1.State
状态机的目的就是完成一系列复杂的状态变化
它的属性应该包含一系列的Transition,也就是图中的边
2.Transition
转变,从图中可以了解到,Transition的属性是 source和target,即起始的状态和结束的状态
- 此时我们考虑一个问题,状态的转变是如何发生的?
经典的状态机例子是自动售货机,里面有一个个的事件,如投币,摇杆等,一个个的时间促成的了状态的转变,因此这次隐含了一个事件的对象
3.Event
事件,一个个事件触发的状态的改变。
不同的事件,可能导致的状态转变是一样的,看图中,Transition4和红边,可以想象有一个天平,原来的状态是平衡,往左边加砝码和右边加砝码,都导致了不平衡
*此时我们在思考一个问题,状态转变的事情会不会有其他影响,还是自动售货机的问题,一系列状态转变后,自动售货机复位了,它吐出了一瓶饮料,这就是发生了其他的影响,我们可以把这些统统叫做动作,即一个事件,可能引发了状态的改变,也可能引发了一系列动作
- 4.Action
动作,由事件带来了影响(不包含状态改变)
二、代码设计
看到网上好多的代码实现,但每个人初衷不同,要不要使用接口,要不要定义实现类等,都是根据实际情况来的,我的建议是,一开始的时候,框架要清晰,其他的尽可能简单的来,后续有需求了,再改造
以下以电灯开关为例
1.首先定义event
状态机中,事件一般都是固定的,比较适合使用枚举类
public enum EventEnum {
TURN_ON("开灯"),TURN_OFF("关灯");
private String name;
EventEnum(String name) {
this.name = name;
}
}
2.定义State
String name;
Map<EventEnum, Transition> map = new HashMap<>();
3.定义Transition
public class Transition {
String name;
State source;
State target;
EventEnum event;
}
4.定义StateMachine
public class StateMachine {
private State currentState;
private State turnOnState=new State("灯亮着");
private State turnOffState=new State("灯不亮");
public StateMachine(State currentState) {
this.currentState = currentState;
init();
}
public StateMachine() {
init();
}
void init() {
Transition t1 = new Transition("t1", turnOnState, turnOffState, EventEnum.TURN_OFF);
turnOnState.getMap().put(t1.getEvent(),t1);
Transition t2 = new Transition("t2", turnOffState, turnOnState, EventEnum.TURN_ON);
turnOffState.getMap().put(t2.getEvent(),t2);
}
State transit(State sourceState,EventEnum event){
Transition t = sourceState.getMap().get(event);
return t.getTarget();
}
State transit(EventEnum event){
Transition t = currentState.getMap().get(event);
return currentState=t.getTarget();
}
}
5.测试案例
public class StateMachineTest {
@Test
void testStateMchine(){
StateMachine machine=new StateMachine();
machine.setCurrentState(machine.getTurnOffState());
System.out.println(machine.transit(EventEnum.TURN_ON).getName());
System.out.println(machine.transit(EventEnum.TURN_OFF).getName());
}
}
灯亮着
灯不亮
二、代码扩展点
第二部分的代码还有很多扩展的点,可以根据实际需求和编码习惯进行改造
但有一点强调一下,状态机里有一个currentState,意味着状态机可以自身保持状态,可以用,也可以不用
比如直接每次使用的时候传入上一次的状态,这样状态就由外界保存,状态机本身无状态。
用的话,就直接传入事件,状态在状态机内保存
此外这个例子,没有涉及到Action,可以简单加个告警的动作,灯亮就告警。我觉得动作属性可以加在state里,这样每到一个状态,如果有action属性,则执行