认识状态机(State Machine)
·
在日常的开发中,我们经常会处理一个流程的各种状态,代码中充斥着大量关于状态的if-else,今天我们来认识一个东西,使你更靠近优雅代码。
概念
状态机(State Machine),全称有限状态机(Finite State Machine, FSM),是计算机科学中一个非常核心的概念。简单来说,它是一个数学模型,用来表示一个对象在其生命周期内所经历的各种状态,以及它是如何根据事件从一个状态切换到另一个状态的。
核心要素
要理解状态机,记住这四个关键词:
- State(状态):对象所处的情况(如:关灯、开灯)。
- Event(事件/触发):导致状态改变的外部输入(如:按了一下开关)。
- Transition(转换):从一个状态切换到另一个状态的动作。
- Action(动作):转换发生时执行的具体逻辑。
例子
自动售货机
自动售货机是状态机的完美体现。我们可以将其简化为以下几个状态:
- 待机(Idle):等待投币。
- 已投币(Has Money):等待选择商品。
- 出货中(Dispensing):正在掉落商品。
- 找零(Returning Change):完成交易。
运作流程:
- 场景 A:你在“待机”状态投了 5 块钱(事件),售货机进入“已投币”状态。
- 场景 B:你在“已投币”状态点击了可乐(事件),机器开始扣款并进入“出货中”状态。
- 约束性:如果你在“待机”状态直接点可乐,状态机没有定义这个“事件”对应的“转换”,所以机器不会有反应。这就是状态机的魅力——它通过硬性的逻辑约束,保证了系统的鲁棒性。
Thread 状态转换
Java 的 Thread 类本身就是一个标准的状态机应用。
- 状态:NEW, RUNNABLE, BLOCKED, WAITING, TIMED_WAITING, TERMINATED。
- 转换:调用 start() 从 NEW 变 RUNNABLE;调用 wait() 进入 WAITING。
为什么要用状态机?
如果你在写代码时发现:
- 存在大量的全局标志位(isPaid, isStarted, isFinished)。
- if-else 逻辑深不见底,修改一处经常导致其他状态逻辑崩坏。
那么,你就该考虑状态机模式了。它能将复杂的逻辑解耦,变成一张清晰的“状态拓扑图”,让代码从“面向过程”升华为“面向数学模型”。
代码
我们以自动售货机为例,演示下状态机的使用。
- 首先,我们定义一个接口,规定所有状态必须具备的动作(事件)。
public interface State {
void insertMoney(); // 投币
void selectProduct(); // 选择商品
void dispense(); // 出货
void returnChange(); // 找零
}
- 实现具体状态类
每个类代表售货机的一个特定状态,并决定在该状态下触发事件会发生什么。
// 1. 待机状态
class IdleState implements State {
private VendingMachine machine;
public IdleState(VendingMachine machine) { this.machine = machine; }
@Override
public void insertMoney() {
System.out.println("已投币,请选择商品。");
machine.setState(machine.getHasMoneyState());
}
@Override
public void selectProduct() { System.out.println("请先投币!"); }
@Override
public void dispense() { System.out.println("请先投币并选择商品!"); }
@Override
public void returnChange() { System.out.println("未投币,无需找零。"); }
}
// 2. 已投币状态
class HasMoneyState implements State {
private VendingMachine machine;
public HasMoneyState(VendingMachine machine) { this.machine = machine; }
@Override
public void insertMoney() { System.out.println("已投币,无需重复投币。"); }
@Override
public void selectProduct() {
System.out.println("商品已选择,正在处理...");
machine.setState(machine.getDispensingState());
machine.dispense(); // 自动进入出货流程
}
@Override
public void dispense() { System.out.println("请先选择商品!"); }
@Override
public void returnChange() {
System.out.println("退币成功。");
machine.setState(machine.getIdleState());
}
}
// 3. 出货中状态
class DispensingState implements State {
private VendingMachine machine;
public DispensingState(VendingMachine machine) { this.machine = machine; }
@Override
public void insertMoney() { System.out.println("请稍候,正在出货..."); }
@Override
public void selectProduct() { System.out.println("请稍候,正在出货..."); }
@Override
public void dispense() {
System.out.println("商品已掉落,请领取。");
machine.setState(machine.getReturningChangeState());
machine.returnChange(); // 自动进入找零流程
}
@Override
public void returnChange() { System.out.println("正在出货,请稍后找零。"); }
}
// 4. 找零状态
class ReturningChangeState implements State {
private VendingMachine machine;
public ReturningChangeState(VendingMachine machine) { this.machine = machine; }
@Override
public void insertMoney() { System.out.println("找零中,请勿投币。"); }
@Override
public void selectProduct() { System.out.println("找零中,无法选择商品。"); }
@Override
public void dispense() { System.out.println("已出货。"); }
@Override
public void returnChange() {
System.out.println("找零完毕,感谢使用。");
machine.setState(machine.getIdleState());
}
}
- 环境上下文(VendingMachine)
这是状态机的持有者,负责维护当前状态并对外暴露接口。
public class VendingMachine {
private State idleState;
private State hasMoneyState;
private State dispensingState;
private State returningChangeState;
private State currentState; // 当前状态
public VendingMachine() {
idleState = new IdleState(this);
hasMoneyState = new HasMoneyState(this);
dispensingState = new DispensingState(this);
returningChangeState = new ReturningChangeState(this);
currentState = idleState; // 初始状态为待机
}
// 设置新状态
public void setState(State state) { this.currentState = state; }
// 暴露给用户的操作
public void insertMoney() { currentState.insertMoney(); }
public void selectProduct() { currentState.selectProduct(); }
public void dispense() { currentState.dispense(); }
public void returnChange() { currentState.returnChange(); }
// Getter方法用于状态转换
public State getIdleState() { return idleState; }
public State getHasMoneyState() { return hasMoneyState; }
public State getDispensingState() { return dispensingState; }
public State getReturningChangeState() { return returningChangeState; }
}
- 运行演示
public class Main {
public static void main(String[] args) {
VendingMachine machine = new VendingMachine();
System.out.println("--- 场景1:正常购买 ---");
machine.insertMoney(); // 待机 -> 已投币
machine.selectProduct(); // 已投币 -> 出货中 -> 找零 -> 待机
System.out.println("\n--- 场景2:非法操作 ---");
machine.selectProduct(); // 此时是待机状态,会提示“请先投币”
System.out.println("\n--- 场景3:投币后反悔 ---");
machine.insertMoney();
machine.returnChange(); // 已投币 -> 待机
}
}
总结:
解耦:售货机类(VendingMachine)不再需要处理复杂的逻辑判断,它只需要把请求“委派”给当前状态对象。
鲁棒性:如果你想在“待机”时执行“出货”,代码逻辑被分散在 IdleState 类中,你只需要在那写一句提示语,系统永远不会进入非法状态。
可扩展性:如果以后增加了“维修中(Maintenance)”状态,你只需要新建一个类并修改少量的转换逻辑,而不需要动原来的长篇大论的 switch 语句。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐

所有评论(0)