1 解决的问题
如果一个对象有多种状态,并且每种状态下的行为不同,一般的做法是在这个对象的各个行为中添加 if-else 或者 switch-case 语句。但更好的做法是为每种状态创建一个状态对象,使用状态对象替换掉这些条件判断语句,使得状态控制更加灵活,扩展性也更好。
2 状态模式
状态模式:允许一个对象在其内部状态改变时改变它的行为,对象看起来似乎修改了自己的类一样。
通俗地说,状态模式就是一个关于多态的设计模式。把事件触发的 状态转移和动作执行,拆分到不同的状态类中,避免了分支判断结构。即状态模式与有限状态机的观念紧密相关。

3 实现代码
- Context (上下文信息类) → 存储当前状态类,并负责具体状态的切换;
- State (抽象状态类) → 定义声明状态更新的操作方法,可以是接口或抽象类;
- ConcreteState (具体状态类) → 实现抽象状态类中定义的方法,根据具体场景指定对应状态改变后的代码逻辑;
// 抽象状态
public abstract class State {
protected StateContext context;
public void setContext(StateContext context) { this.context = context; }
abstract void onHomeClick();
abstract void onPowerClick();
abstract void onVolumeAscClick();
abstract void onVolumeDescClick();
}
// 具体状态 → 关机状态
public class CloseState extends State {
@Override public void onHomeClick() { System.out.println("处于关机状态,按Home键没有反应"); }
@Override void onPowerClick() {
System.out.println("手机开机");
context.setState(FirstBootState.class);
context.setScreenOn(true);
context.getState().onHomeClick();
}
@Override void onVolumeAscClick() { System.out.println("处于关机状态,按音量+没反应"); }
@Override void onVolumeDescClick() { System.out.println("处于关机状态,按音量-没反应"); }
}
// 具体状态 → 第一次启动状态
public class FirstBootState extends State {
@Override public void onHomeClick() {
System.out.println("首次启动,可以进行密码解锁");
System.out.println("解锁完毕,进入主界面");
context.setState(AfterBootState.class);
context.setScreenOn(true);
}
@Override void onPowerClick() {
if(context.isScreenOn()) {
System.out.println("熄屏");
} else {
System.out.println("亮屏,等待密码解锁");
}
context.setScreenOn(!context.isScreenOn());
}
@Override void onVolumeAscClick() { System.out.println("音量+"); }
@Override void onVolumeDescClick() { System.out.println("音量-"); }
}
// 具体状态 → 非第一次启动状态
public class NotFirstBootState extends State {
@Override public void onHomeClick() {
System.out.println("非首次启动,可以通过密码或指纹解锁");
System.out.println("解锁完毕,进入主界面");
context.setScreenOn(true);
context.setState(AfterBootState.class);
}
@Override void onPowerClick() {
if(context.isScreenOn()) {
System.out.println("熄屏");
} else {
System.out.println("亮屏,等待密码或指纹解锁");
context.setState(NotFirstBootState.class);
}
context.setScreenOn(!context.isScreenOn());
}
@Override void onVolumeAscClick() { System.out.println("音量+"); }
@Override void onVolumeDescClick() { System.out.println("音量-"); }
}
// 具体状态 → 启动后
public class AfterBootState extends State {
@Override void onHomeClick() { System.out.println("返回主界面"); }
@Override void onPowerClick() {
if(context.isScreenOn()) {
System.out.println("熄屏");
context.setState(NotFirstBootState.class);
} else {
System.out.println("亮屏,等待密码或指纹解锁");
context.getState().onHomeClick();
}
context.setScreenOn(!context.isScreenOn());
}
@Override void onVolumeAscClick() { System.out.println("音量+"); }
@Override void onVolumeDescClick() { System.out.println("音量-"); }
}
// 上下文信息类
public class StateContext {
private boolean isScreenOn = false; // 屏幕是否亮着
public final static Map<Class, State> stateMap = new HashMap<>();
private State state; // 手机当前状态
static {
stateMap.put(CloseState.class, new CloseState());
stateMap.put(FirstBootState.class, new FirstBootState());
stateMap.put(NotFirstBootState.class, new NotFirstBootState());
stateMap.put(AfterBootState.class, new AfterBootState());
}
public void setState(Class stateClass) {
this.state = stateMap.get(stateClass);
this.state.setContext(this);
}
public State getState() { return state; }
public boolean isScreenOn() { return isScreenOn; }
public void setScreenOn(boolean screenOn) {
isScreenOn = screenOn;
System.out.println("===> 屏幕处于:" + (isScreenOn ? "亮屏状态": "熄屏状态"));
}
}
// 测试用例
public class StateTest {
public static void main(String[] args) {
StateContext context = new StateContext();
context.setState(CloseState.class);
// 处于关机状态点击音量- 和 home键
context.getState().onVolumeDescClick();
context.getState().onHomeClick();
// 处于关机状态点击电源键
context.getState().onPowerClick();
context.getState().onPowerClick();
context.getState().onHomeClick();
context.getState().onVolumeAscClick();
}
}
4 优缺点
优点:
- 单一职责原则:将与特定状态相关的代码组织到单独的类中;
- 更好的扩展性:扩展新的状态只需增加实现类,在需要维护的地方设置下新状态即可;
- 避免条件判断的状态机条件;
缺点:
- 类增加,每个状态对应一个具体状态类;
- 不满足开闭原则。虽然增加新状态不用修改其他类,但是如果存在旧状态到新状态的状态切换联系时,还是需要修改其他类。
- 逻辑零散,无法在一个地方就看出整个状态机的转换逻辑;
5 应用场景
- 对象根据自身不同的状态对一个响应做出不同的反应,并且状态的数量比较多;
- 某个类需要根据成员变量的当前值改变自身行为,从而需要大量条件判断时;
6 有限状态机的概念
英文翻译 Finite State Machine,缩写 FSM,简称状态机,它有三个组成部分:状态(State)、事件(Event)、动作(Action)。其中的事件又称为 转移条件,事件触发状态的转移和动作的执行(非必须)。
也可以理解为一种数学模型,该模型中有几个状态(有限的),在不同场景下,不同的状态间发生转移,在状态转移过程中可能伴随着不同的事件发生。
状态机有三种常见的实现方式:
- 分支逻辑法 → 缺点是改变业务逻辑,改起来容易出错,代码也不易看懂。适合简单状态机;
- 查表法 → 适用于状态很多、状态转移比较复杂的状态机,用二维数组表示状态转移图,可极大提高代码的可读性与可维护性;
- 状态模式 → 适用于状态并不多、状态转移较简单,事件触发动作包含的业务逻辑可能较复杂的状态机。