状态模式的核心思想是将对象的状态封装成独立的类,当对象的状态发生改变时,其行为也会随之改变,让对象看起来好像修改了自身的类。这种模式非常适合处理对象有多个状态,且不同状态下行为差异明显的场景,比如订单状态流转、电梯运行状态控制等。

状态模式的核心组成
一个标准的状态模式结构包含三个核心角色:
- 环境类(Context):维护当前状态的实例,对外提供操作接口,状态改变时由它触发状态切换。
- 抽象状态类(State):定义所有状态共有的行为接口,不同具体状态类实现该接口的不同逻辑。
- 具体状态类(ConcreteState):继承或实现抽象状态类,实现对应状态下的具体行为,同时可以定义状态转换的规则。
状态转换的两种实现方案
方案一:在环境类中控制状态转换
这种方案把状态转换的判断逻辑放在环境类中,具体状态类只负责实现当前状态下的行为,不关心状态怎么切换。适合状态转换规则比较固定,或者多个状态类共享相同转换逻辑的场景。
下面以电梯运行为例,电梯有停止、运行、故障三个状态,我们用这种方案实现状态管理:
// 抽象状态类
interface ElevatorState {
// 上升行为
void up(ElevatorContext context);
// 下降行为
void down(ElevatorContext context);
// 停止行为
void stop(ElevatorContext context);
}
// 停止状态
class StopState implements ElevatorState {
@Override
public void up(ElevatorContext context) {
System.out.println("电梯从停止状态开始上升");
// 切换到运行状态
context.setState(new RunState());
}
@Override
public void down(ElevatorContext context) {
System.out.println("电梯从停止状态开始下降");
context.setState(new RunState());
}
@Override
public void stop(ElevatorContext context) {
System.out.println("电梯已经处于停止状态,无需操作");
}
}
// 运行状态
class RunState implements ElevatorState {
@Override
public void up(ElevatorContext context) {
System.out.println("电梯正在上升中,无需重复操作");
}
@Override
public void down(ElevatorContext context) {
System.out.println("电梯正在下降中,无需重复操作");
}
@Override
public void stop(ElevatorContext context) {
System.out.println("电梯从运行状态切换到停止状态");
context.setState(new StopState());
}
}
// 故障状态
class FaultState implements ElevatorState {
@Override
public void up(ElevatorContext context) {
System.out.println("电梯故障,无法上升");
}
@Override
public void down(ElevatorContext context) {
System.out.println("电梯故障,无法下降");
}
@Override
public void stop(ElevatorContext context) {
System.out.println("电梯故障,已停止运行");
}
}
// 环境类
class ElevatorContext {
private ElevatorState currentState;
public ElevatorContext() {
// 初始状态为停止状态
this.currentState = new StopState();
}
public void setState(ElevatorState state) {
this.currentState = state;
}
public void up() {
currentState.up(this);
}
public void down() {
currentState.down(this);
}
public void stop() {
currentState.stop(this);
}
}
// 测试类
public class StatePatternDemo {
public static void main(String[] args) {
ElevatorContext elevator = new ElevatorContext();
elevator.up();
elevator.down();
elevator.stop();
// 模拟故障切换
elevator.setState(new FaultState());
elevator.up();
}
}
方案二:在具体状态类中控制状态转换
这种方案把状态转换的逻辑放到具体状态类内部,环境类只需要调用状态的行为接口,不需要关心状态切换的细节。适合每个状态转换规则差异大,或者状态转换逻辑和当前状态强相关的场景。
我们继续用电梯的例子,调整实现方式:
// 抽象状态类
interface ElevatorState {
void up(ElevatorContext context);
void down(ElevatorContext context);
void stop(ElevatorContext context);
// 新增故障触发方法
void triggerFault(ElevatorContext context);
}
// 停止状态
class StopState implements ElevatorState {
@Override
public void up(ElevatorContext context) {
System.out.println("电梯从停止状态开始上升");
context.setState(new RunState());
}
@Override
public void down(ElevatorContext context) {
System.out.println("电梯从停止状态开始下降");
context.setState(new RunState());
}
@Override
public void stop(ElevatorContext context) {
System.out.println("电梯已经处于停止状态");
}
@Override
public void triggerFault(ElevatorContext context) {
System.out.println("停止状态的电梯触发故障");
context.setState(new FaultState());
}
}
// 运行状态
class RunState implements ElevatorState {
@Override
public void up(ElevatorContext context) {
System.out.println("电梯正在上升中");
}
@Override
public void down(ElevatorContext context) {
System.out.println("电梯正在下降中");
}
@Override
public void stop(ElevatorContext context) {
System.out.println("电梯从运行状态切换到停止状态");
context.setState(new StopState());
}
@Override
public void triggerFault(ElevatorContext context) {
System.out.println("运行状态的电梯触发故障");
context.setState(new FaultState());
}
}
// 故障状态
class FaultState implements ElevatorState {
@Override
public void up(ElevatorContext context) {
System.out.println("故障电梯无法上升");
}
@Override
public void down(ElevatorContext context) {
System.out.println("故障电梯无法下降");
}
@Override
public void stop(ElevatorContext context) {
System.out.println("故障电梯已停止");
}
@Override
public void triggerFault(ElevatorContext context) {
System.out.println("电梯已经处于故障状态");
}
}
// 环境类
class ElevatorContext {
private ElevatorState currentState;
public ElevatorContext() {
this.currentState = new StopState();
}
public void setState(ElevatorState state) {
this.currentState = state;
}
public void up() {
currentState.up(this);
}
public void down() {
currentState.down(this);
}
public void stop() {
currentState.stop(this);
}
public void triggerFault() {
currentState.triggerFault(this);
}
}
// 测试类
public class StatePatternDemo2 {
public static void main(String[] args) {
ElevatorContext elevator = new ElevatorContext();
elevator.up();
elevator.triggerFault();
elevator.down();
}
}
两种方案的对比与选择
我们可以通过下表对比两种方案的特点,根据实际场景选择:
| 对比维度 | 环境类控制状态转换 | 具体状态类控制状态转换 |
|---|---|---|
| 状态转换逻辑位置 | 环境类 | 具体状态类 |
| 状态类耦合度 | 低,状态类不需要依赖其他状态 | 高,状态类可能需要知道其他状态的存在 |
| 扩展性 | 新增状态时可能需要修改环境类的转换逻辑 | 新增状态时只需要实现对应行为,不需要修改环境类 |
| 适用场景 | 状态转换规则统一、简单 | 状态转换规则复杂、和状态强相关 |
行为随状态改变的实现要点
要让对象的行为跟随状态自动改变,需要注意以下几个要点:
- 所有状态相关的行为都要定义在抽象状态接口中,避免遗漏。
- 环境类只持有当前状态的引用,不直接实现状态相关的行为,把行为委托给当前状态对象执行。
- 状态转换时只需要修改环境类持有的状态实例,后续的行为调用会自动使用新状态的逻辑。
- 如果状态转换需要依赖外部条件,可以把条件判断放在状态转换的逻辑中,而不是放在环境类的业务代码里。
状态模式通过封装状态和行为,有效避免了大量的if-else或者switch-case判断,让代码结构更清晰,后续新增状态或者修改状态行为时,只需要新增或修改对应的状态类,不需要改动其他已有代码,符合开闭原则。在实际开发中,遇到对象状态多、状态行为差异大的场景,都可以优先考虑使用状态模式来实现状态转换和行为管理。