1. 概述

本文将带你了解状态机的概念,以及如何使用 Java 枚举(Enum)来实现一个状态机。

我们还会对比使用接口和具体类的方式,说明枚举实现的优势。

2. Java 枚举

Java 枚举是一种特殊的类,用于定义一组常量。它提供了类型安全更清晰的代码结构

举个例子,假设我们有一个 HR 系统,员工提交请假请求后,需要由团队 Leader 审核,再由部门经理批准。

最简单的枚举如下:

public enum LeaveRequestState {
    Submitted,
    Escalated,
    Approved
}

我们可以直接引用这些状态:

LeaveRequestState state = LeaveRequestState.Submitted;

枚举中也可以定义方法。 我们可以在枚举中定义一个抽象方法,强制每个枚举实例都实现该方法。这个特性对状态机的实现非常关键。

⚠️ 注意:Java 枚举默认继承自 java.lang.Enum,因此不能继承其他类,但可以实现接口。

来看一个包含抽象方法的枚举示例:

public enum LeaveRequestState {
    Submitted {
        @Override
        public String responsiblePerson() {
            return "Employee";
        }
    },
    Escalated {
        @Override
        public String responsiblePerson() {
            return "Team Leader";
        }
    },
    Approved {
        @Override
        public String responsiblePerson() {
            return "Department Manager";
        }
    };

    public abstract String responsiblePerson();
}

注意最后一个枚举常量后的分号 ; 是必须的,因为后面还有方法定义。

在这个例子中,我们为每个状态指定了负责人:

LeaveRequestState state = LeaveRequestState.Escalated;
assertEquals("Team Leader", state.responsiblePerson());
LeaveRequestState state = LeaveRequestState.Approved;
assertEquals("Department Manager", state.responsiblePerson());

3. 状态机

状态机(也叫有限状态机或有限自动机)是一种用于构建抽象机器的计算模型。它在任意时刻只能处于一个状态,状态之间的转换称为“状态转移”。

虽然数学上可以用状态图和公式来表示,但对我们程序员来说,只要掌握核心思想即可。

状态模式(State Pattern)是 GoF 23 种设计模式之一,它借鉴了数学模型的思想:让对象根据当前状态封装不同的行为。我们可以先定义状态之间的转移逻辑,再分别实现每个状态。

接下来,我们会基于请假流程的例子,实现一个状态机。

4. 使用枚举实现状态机

我们重点来看如何用 Java 枚举实现状态机。虽然也有其他实现方式,但枚举实现是最简洁的一种。

枚举实现的核心思想是:我们不需要手动管理状态的切换,只需要定义好从一个状态到另一个状态的转移逻辑。

来看代码:

public enum LeaveRequestState {

    Submitted {
        @Override
        public LeaveRequestState nextState() {
            return Escalated;
        }

        @Override
        public String responsiblePerson() {
            return "Employee";
        }
    },
    Escalated {
        @Override
        public LeaveRequestState nextState() {
            return Approved;
        }

        @Override
        public String responsiblePerson() {
            return "Team Leader";
        }
    },
    Approved {
        @Override
        public LeaveRequestState nextState() {
            return this;
        }

        @Override
        public String responsiblePerson() {
            return "Department Manager";
        }
    };

    public abstract LeaveRequestState nextState(); 
    public abstract String responsiblePerson();
}

这里我们通过 nextState() 方法定义了状态之间的转移逻辑。每个枚举实例都实现了这个方法,表示如何进入下一个状态。

下面是测试代码:

LeaveRequestState state = LeaveRequestState.Submitted;

state = state.nextState();
assertEquals(LeaveRequestState.Escalated, state);

state = state.nextState();
assertEquals(LeaveRequestState.Approved, state);

state = state.nextState();
assertEquals(LeaveRequestState.Approved, state);

✅ 我们从 Submitted 状态开始,依次调用 nextState() 方法完成状态转移。

⚠️ 注意:Approved 是终止状态,调用 nextState() 后不会再变化。

5. 使用枚举实现状态机的优势

相比使用接口 + 实现类的方式,枚举实现有以下优势:

  • 代码更简洁:所有状态和转移逻辑都集中在枚举中
  • 类型安全:避免非法状态的存在
  • 易于维护:逻辑集中,改动影响范围小
  • 可读性强:枚举本身就是对状态的清晰表达

6. 总结

本文介绍了状态机的基本概念,并演示了如何用 Java 枚举实现一个简单的请假审批状态机。

相比传统的接口实现方式,枚举实现更加干净、直观,适合处理状态不多、逻辑简单的业务场景。

所有代码示例都可以在我们的 GitHub 仓库 中找到。


原始标题:Implementing Simple State Machines with Java Enums