状态机

368 阅读3分钟

Java有限状态机FSM(基础篇)

前言

在java后端项目的开发过程中,经常会碰到需要进行状态转换的场景,比如订单的状态、任务的状态等等,通常会使用枚举对状态进行定义,并在需要时更改其枚举值。

但是在一些比较复杂的场景下,例如状态数大于等于5个,或者大状态拥有子状态的场景(如订单【交易中】的状态包含【待发货】【已发货】等子状态),再直接通过修改枚举值的方式已经难以进行维护,且会导致状态变更的代码在项目中散布,不仅在实现上不够优雅,而且在需要添加或者修改状态时牵一发而动全身。因此,我们需要有一种技术手段,来对状态进行统一管理,以及收束变更状态的代码入口。

有限状态机(Finite State Machine, FSM)就是用来干这个事的!

该文是一个系列文章,本章先介绍基本概念及其简单实现

状态机就是包含多个状态的数学模型,并可以在状态之间进行变换并且触发一些动作。

一个状态机一般包含以下几个元素

  1. State 当前状态
  2. Event 触发事件
  3. Transition 状态变换,或者说下一个状态(次态)
  4. Action 要执行的动作

如何实现一个简单的状态机

这里根据多篇参考文档,总结以下四种实现方式并进行简述,具体的实现代码可以查看参考文档2

1. switch...case

  • 保存当前状态
  • 状态变更时传入变更事件event, 先通过if...else 判断是哪个事件,再对当前事件 event 进行 switch...case 确定要执行的操作
  • 进入下一个状态,并执行 action

简单代码如下

public class StateMachine {
    // 当前状态
    private State currState;
    
    public StateMachine(State initState) {
        this.currState = initState;
    }
    
    public void transist(Event event) {
        if (当前是状态1) {
            switch (event) {
                case 事件1:
                    // 设置下一个状态
                    // 执行action
                case 事件2:
                  ...
            }
        }
        
        // ......
    }
}

该种方法实现是最简单的实现方法,在状态数量较少时,这是一个很高效的实现方案。但是在较复杂的状态下,会导致嵌套大量的 if...else 和 switch...case 语句,维护起来费劲而且实现不够优雅。

2. 状态模式 (State Design Pattern)

状态模式主要就是对转换规则进行封装,封装状态而暴露行为,状态的改变看起来就是行为发生改变,总结起来,状态模式干了这么几件事 需要定义状态抽象类 AbstractState,其中需要包含上下文 Context, 以及所有的抽象事件(event)对象的方法

public abstract class AbstractState { 

// 上下文信息 
protected Context context; 

public void setContext(Context context) { 
this.context = context;
} 
// 事件操作放在具体的状态定义内 
abstract void event1();
abstract void event2(); 
// ...... }

具体的状态需要继承并实现抽象状态

public class State1 extends AbstractState {
    @Override
    void event1() {
        // set next state
        // do something else
    }
    // ......
}

上下文 Context 中需要包含所有所需信息,包括所有的状态实例,并指定一个属性指示当前是哪个状态实例

public class Context {

    // 当前状态实例
    protected AbstractState currState;
    protected static final State1 state1 = new State1();
    protected static final State2 state2 = new State2();
    // ......

    public Context(AbstractState state) {
        setState(state);
    }

    // 具体的事件调用当前状态进行执行
    public void event1(){
        this.currState.event1();
    }

    // ......
}

由上可见,状态模式将与行为与状态相绑定,避免了直接去写大量的 if...else 和 switch...case 编写时可以方便地增加新的状态,并且只需要改变对象的状态就可以改变对象的行为。、

参考:

zhuanlan.zhihu.com/p/97442825

www.jianshu.com/p/e335799a5…