状态模式 (State)
场景问题
问题案例
考虑一个电梯系统,电梯有开门、关门、运行、停止等多种状态,不同状态下对外的操作行为不同。
不用模式的解决方案
实现简化版本
最直接的做法是用if-else语句判断当前状态:
public class Elevator {
private String state = "stopped";
public void open() {
if ("stopped".equals(state)) {
System.out.println("电梯门打开");
state = "opened";
}
}
public void close() {
if ("opened".equals(state)) {
System.out.println("电梯门关闭");
state = "closed";
}
}
public void run() {
if ("closed".equals(state)) {
System.out.println("电梯运行");
state = "running";
}
}
public void stop() {
if ("running".equals(state)) {
System.out.println("电梯停止");
state = "stopped";
}
}
}
使用示例
Elevator elevator = new Elevator();
elevator.open();
elevator.close();
elevator.run();
elevator.stop();
有何问题
- 状态判断分散在各方法中,难以维护。
- 新增状态或行为需修改大量代码。
- 违反开闭原则,扩展性差。
解决方案
状态模式的定义
允许对象在其内部状态发生改变时改变它的行为,对象看起来好像修改了它的类。
应用状态模式的思路
- 定义状态接口,每种状态实现具体行为。
- 上下文持有当前状态对象,行为委托给状态对象。
- 切换状态只需更换状态对象,无需修改上下文代码。
状态模式的结构
- State:抽象状态,定义所有行为接口。
- OpenedState/ClosedState/RunningState/StoppedState:具体状态,实现具体行为。
- ElevatorContext:上下文,持有当前状态,行为委托给状态对象。
状态模式的实现
// 状态接口
public interface State {
void open(ElevatorContext ctx);
void close(ElevatorContext ctx);
void run(ElevatorContext ctx);
void stop(ElevatorContext ctx);
}
// 上下文
public class ElevatorContext {
private State state;
public void setState(State state) { this.state = state; }
public void open() { state.open(this); }
public void close() { state.close(this); }
public void run() { state.run(this); }
public void stop() { state.stop(this); }
}
// 具体状态
public class OpenedState implements State {
public void open(ElevatorContext ctx) { System.out.println("电梯门已打开"); }
public void close(ElevatorContext ctx) {
System.out.println("电梯门关闭");
ctx.setState(new ClosedState());
}
public void run(ElevatorContext ctx) { System.out.println("门开着不能运行"); }
public void stop(ElevatorContext ctx) { System.out.println("门开着已停止"); }
}
public class ClosedState implements State {
public void open(ElevatorContext ctx) {
System.out.println("电梯门打开");
ctx.setState(new OpenedState());
}
public void close(ElevatorContext ctx) { System.out.println("电梯门已关闭"); }
public void run(ElevatorContext ctx) {
System.out.println("电梯运行");
ctx.setState(new RunningState());
}
public void stop(ElevatorContext ctx) { System.out.println("电梯已停止"); }
}
public class RunningState implements State {
public void open(ElevatorContext ctx) { System.out.println("运行中不能开门"); }
public void close(ElevatorContext ctx) { System.out.println("运行中门已关闭"); }
public void run(ElevatorContext ctx) { System.out.println("电梯正在运行"); }
public void stop(ElevatorContext ctx) {
System.out.println("电梯停止");
ctx.setState(new StoppedState());
}
}
public class StoppedState implements State {
public void open(ElevatorContext ctx) {
System.out.println("电梯门打开");
ctx.setState(new OpenedState());
}
public void close(ElevatorContext ctx) { System.out.println("电梯门已关闭"); }
public void run(ElevatorContext ctx) {
System.out.println("电梯运行");
ctx.setState(new RunningState());
}
public void stop(ElevatorContext ctx) { System.out.println("电梯已停止"); }
}
使用示例
public class Client {
public static void main(String[] args) {
ElevatorContext ctx = new ElevatorContext();
ctx.setState(new StoppedState());
ctx.open();
ctx.close();
ctx.run();
ctx.stop();
}
}
状态重写示例
示例结构
示例代码
见上文"状态模式的实现"部分。
模式讲解
状态模式的功能
- 封装状态切换逻辑,简化上下文代码。
- 新增状态无需修改上下文,扩展性强。
- 满足开闭原则。
实现成接口
- 状态通常定义为接口或抽象类。
使用工厂方法
- 可结合工厂方法模式,统一创建状态对象。
切换产品簇
- 可用于切换不同的状态实现,如不同设备、流程状态。