基本概念

状态模式:是一种行为型设计模式,允许对象在内部状态改变时改变它的行为,对象看起来似乎修改了它的类。

核心思想:将状态抽象为独立的类,上下文对象持有状态对象的引用,当状态改变时,只需更换状态对象,从而改变上下文的行为,避免使用大量条件判断语句。

核心结构

  1. State(状态接口):定义所有具体状态必须实现的接口,声明状态相关的方法
  2. ConcreteState(具体状态):实现状态接口,定义特定状态下的行为
  3. Context(上下文):持有当前状态对象的引用,将状态相关的请求委托给当前状态对象处理

示例说明

场景:设计一个手机播放器,具有”播放”、”暂停”、”停止”三种状态,不同状态下点击按钮会有不同行为。

  1. 状态接口
1
2
3
4
5
6
7

public interface PlayerState {
void clickPlay(); // 点击播放
void clickPause(); // 点击暂停
void clickStop(); // 点击停止
}

  1. 播放状态
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

public class PlayingState implements PlayerState {
@Override
public void clickPlay() {
System.out.println("正在播放,点击播放无效");
}

@Override
public void clickPause() {
System.out.println("从播放状态切换到暂停状态");
// 切换到暂停状态
PlayerContext.setState(new PausedState());
}

@Override
public void clickStop() {
System.out.println("从播放状态切换到停止状态");
// 切换到停止状态
PlayerContext.setState(new StoppedState());
}
}

  1. 暂停状态
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

public class PausedState implements PlayerState {
@Override
public void clickPlay() {
System.out.println("从暂停状态切换到播放状态");
// 切换到播放状态
PlayerContext.setState(new PlayingState());
}

@Override
public void clickPause() {
System.out.println("正在暂停,点击暂停无效");
}

@Override
public void clickStop() {
System.out.println("从暂停状态切换到停止状态");
// 切换到停止状态
PlayerContext.setState(new StoppedState());
}
}

  1. 停止状态
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

public class StoppedState implements PlayerState {
@Override
public void clickPlay() {
System.out.println("从停止状态切换到播放状态");
// 切换到播放状态
PlayerContext.setState(new PlayingState());
}

@Override
public void clickPause() {
System.out.println("未播放,点击暂停无效");
}

@Override
public void clickStop() {
System.out.println("已停止,点击停止无效");
}
}

  1. 上下文类(播放器)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

public class PlayerContext {
private static PlayerState state;

// 设置状态
public static void setState(PlayerState newState) {
state = newState;
}

// 委托状态处理请求
public static void clickPlay() {
state.clickPlay();
}

public static void clickPause() {
state.clickPause();
}

public static void clickStop() {
state.clickStop();
}
}

  1. 验证测试
1
2
3
4
5
6
7
8
9
10
11
12
13
14

public class Test {
public static void main(String[] args) {
// 初始状态为停止状态
PlayerContext.setState(new StoppedState());

System.out.println("===== 播放器状态测试 =====");
PlayerContext.clickPlay(); // 停止→播放
PlayerContext.clickPause(); // 播放→暂停
PlayerContext.clickPlay(); // 暂停→播放
PlayerContext.clickStop(); // 播放→停止
PlayerContext.clickPause(); // 停止状态下点击暂停无效
}
}
  1. 执行结果:
1
2
3
4
5
6
7
8
9

===== 播放器状态测试 =====
从停止状态切换到播放状态
从播放状态切换到暂停状态
从暂停状态切换到播放状态
从播放状态切换到停止状态
未播放,点击暂停无效
总结

对象在内部状态改变时改变其行为:状态模式将状态封装进分离的类,并把行为委托当前状态的对象,行为随着内部状态而改变。
对象看起来好像改变了它的类:使用组合来简单引用不同的状态对象,造成类改变的假象。

类图如下:
状态模式

总结

优点

  • 封装状态逻辑:状态转换逻辑被封装在具体状态类中,代码更清晰;
  • 符合开闭原则:新增状态只需添加新的状态类,无需修改原有代码;
  • 状态解耦:状态之间相互独立,减少了条件判断语句(如if-else);
  • 行为动态变化:上下文对象的行为随状态切换而动态改变;

缺点

  • 类数量增加:每个状态需要独立的类,可能导致类数量过多;
  • 状态转换复杂性:复杂状态转换逻辑可能需要在状态类或上下文中维护;
  • 上下文与状态耦合:上下文需要知道所有具体状态类,存在一定耦合;

使用场景

  • 状态机系统:如工作流引擎、游戏角色状态、电梯状态控制
  • 界面状态管理:如按钮的启用/禁用状态、对话框的显示/隐藏状态
  • 设备状态控制:如打印机的就绪/打印/错误状态
  • 订单状态管理:如电商系统中订单的创建/支付/发货/完成状态

与其他模式的对比

与策略模式的区别:

  • 策略模式的状态切换由客户端控制,状态模式的状态切换由状态内部逻辑控制;
  • 策略模式关注算法的选择,状态模式关注状态的变化

与责任链模式的区别:

  • 责任链模式处理请求的对象链是固定的,状态模式的状态切换是动态的;
  • 责任链模式关注请求的传递,状态模式关注状态相关的行为变化;