状态设计模式是一种行为设计模式,它允许对象在其内部状态更改时更改其行为,就像它在运行时切换到不同的类一样。
它在以下情况下特别有用:
对象可以处于许多不同状态之一,每种状态都有不同的行为。
对象的行为取决于当前上下文,并且该上下文会随着时间的推移而变化。
你希望避免检查每个可能状态的大型、整体式或语句。if-elseswitch
当面对这种情况时,开发人员通常首先在类中使用条件逻辑来切换基于状态变量的行为。
例如,类 可能会使用 块来确定要执行的作,具体取决于它是处于"草稿"、"审阅"还是"已发布"状态。Documentif-else
但随着状态数量的增长,这种方法变得难以扩展、难以测试,并且违反了开放/封闭原则------任何新状态都需要修改现有逻辑,从而增加了破坏当前功能的风险。
状态模式通过将每个状态封装到自己的类中,并让上下文对象将行为委托给当前状态对象来解决这个问题。这使您的代码更易于扩展、重用和维护,而不会因条件而使核心逻辑变得混乱。
让我们通过一个真实世界的示例来了解如何应用状态模式以干净、可扩展和面向对象的方式管理动态行为。
问题:管理自动售货机状态
想象一下,您正在构建一个简单的自动售货机系统。从表面上看,这似乎是一项简单的任务:接受资金,分配产品,然后回到闲置状态。
但在幕后,机器的行为需要根据其当前状态而变化。

在任何给定时间,自动售货机只能处于一种状态,例如:
IdleState -- 等待用户输入(未选择任何内容,未插入任何资金)。
ItemSelectedState -- 已选择项目,等待付款。
HasMoneyState -- 已插入资金,等待分配所选项目。
DispensingState -- 机器正在主动分配项目。
该机器支持一些面向用户的作:
selectItem(String itemCode)-- 选择要购买的商品
insertCoin(double amount)-- 插入所选项目的付款
dispenseItem()-- 触发物品分配过程
这些方法中的每一种都应根据计算机的当前状态以不同的方式运行。
例如:
在 机器打开时 调用(未选择任何项目,未插入任何资金)不应执行任何作或显示错误。dispenseItem()IdleState
在选择项目之前调用可能会被禁止或排队。insertCoin()
应 忽略或推迟期间调用,直到项目被分配。selectItem()DispensingState
天真的方法
一种常见但有缺陷的方法是 使用 or 语句在整体类中手动管理状态转换:VendingMachineif-elseswitch
public class VendingMachine {
private enum State {
IDLE, ITEM_SELECTED, HAS_MONEY, DISPENSING
}
private State currentState = State.IDLE;
private String selectedItem = "";
private double insertedAmount = 0.0;
public void selectItem(String itemCode) {
switch (currentState) {
case IDLE:
selectedItem = itemCode;
currentState = State.ITEM_SELECTED;
break;
case ITEM_SELECTED:
System.out.println("Item already selected");
break;
case HAS_MONEY:
System.out.println("Payment already received for item");
break;
case DISPENSING:
System.out.println("Currently dispensing");
break;
}
}
public void insertCoin(double amount) {
switch (currentState) {
case IDLE:
System.out.println("No item selected");
break;
case ITEM_SELECTED:
insertedAmount = amount;
System.out.println("Inserted $" + amount + " for item");
currentState = State.HAS_MONEY;
break;
case HAS_MONEY:
System.out.println("Money already inserted");
break;
case DISPENSING:
System.out.println("Currently dispensing");
break;
}
}
public void dispenseItem() {
switch (currentState) {
case IDLE:
System.out.println("No item selected");
break;
case ITEM_SELECTED:
System.out.println("Please insert coin first");
break;
case HAS_MONEY:
System.out.println("Dispensing item '" + selectedItem);
currentState = State.DISPENSING;
// Simulate delay and completion
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
System.out.println("Item dispensed successfully.");
resetMachine();
break;
case DISPENSING:
System.out.println("Already dispensing. Please wait.");
break;
}
}
private void resetMachine() {
selectedItem = "";
insertedAmount = 0.0;
currentState = State.IDLE;
}
}
这种方法有什么问题?
虽然使用 with 语句适用于小型、可预测的系统,但这种方法不能很好地扩展。enumswitch
-
- 代码混乱
所有与状态相关的逻辑都塞进一个类 () 中,导致每个方法( 、 、 等)都出现大而重复的块。VendingMachineswitchif-elseselectItem()insertCoin()dispenseItem()
- 代码混乱
这导致:
难以阅读和推理的代码
跨多个方法重复检查状态
多个开发人员接触同一文件时的逻辑脆弱
-
- 难以扩展
假设你想引入新的状态,例如:
OutOfStockState-- 当所选商品售罄时
MaintenanceState-- 机器正在维修时
要支持这些,您需要:
更新 每个方法中的每个开关块
在多个位置添加逻辑
破坏现有功能的风险
这违反了开放/封闭原则------当系统应该开放扩展时,系统可以进行修改。
- 难以扩展
-
- 违反单一责任原则
该 班现在负责:VendingMachine
管理状态转换
实施业务规则
执行特定于状态的逻辑
这种紧密耦合使该类成为单片、难以测试且耐变化。
- 违反单一责任原则
我们真正需要什么
我们需要将与每个状态关联的行为封装到它自己的类中------这样自动售货机就可以将工作委托给当前状态对象,而不是在内部管理它。
这将使我们能够:
避免疯狂的开关情况
添加或删除状态而不修改核心类
保持每个状态的逻辑隔离和可重用
这正是状态设计模式所实现的。
状态模式
State 模式允许对象(Context)在其内部状态更改时更改其行为。该对象似乎更改了其类,因为它的行为现在已委托给不同的状态对象。
状态模式不是将特定于状态的行为嵌入上下文类本身,而是将该行为提取到单独的类中,每个类代表一个不同的状态。上下文对象保存对状态对象的引用,并将其作委托给它。
这会产生更干净、更模块化和可扩展的代码。
定义一个 State 接口(或抽象类),该接口声明表示 Context 可以执行的作的方法。
创建 ConcreteState 类,每个类都实现 State 接口。每个 ConcreteState 类都实现特定于 Context 的特定状态的行为。
Context类维护定义其当前状态的ConcreteState子类的实例。
在 Context 上调用作时,它会将该作委托给其当前 State 对象。
ConcreteState 对象通常负责将 Context 转换为新状态。
类图

-
- 状态接口(例如MachineState)
声明与上下文支持的作相对应的方法(例如,, ,)。selectItem()insertCoin()dispenseItem()
这些方法通常将上下文作为参数,因此状态可以触发转换或作上下文数据。
作为所有具体状态的共同合同。
- 状态接口(例如MachineState)
-
- 具体状态(例如, IdleStateItemSelectedState)
实现接口 。State
为 每个作定义特定于状态的行为。
通常负责 在发生特定作时将上下文转换为另一种状态。
还可以包括特定于状态的逻辑(例如,验证、消息传递)。
- 具体状态(例如, IdleStateItemSelectedState)
-
- 上下文(例如VendingMachine)
维护对当前对象的引用 。State
定义每个作(、 等)的方法。selectItem()insertCoin()
委托对当前状态的调用 --- 状态对象处理逻辑。
提供一种方法,例如允许状态之间的转换。setState()
- 上下文(例如VendingMachine)
实现状态模式
我们将应用状态模式来分离关注点,并使自动售货机更易于管理和扩展,而不是使用 or 语句将状态转换和行为硬编码为单个整体类。if-elseswitch
第一步是定义一个 接口,用于声明自动售货机支持的所有作。MachineState
-
- 定义状态接口
该接口表示所有州都必须遵循的契约。它声明与自动售货机面向用户的作相对应的方法。
public interface MachineState {
void selectItem(VendingMachine context, String itemCode);
void insertCoin(VendingMachine context, double amount);
void dispenseItem(VendingMachine context);
} - 定义状态接口
每个状态都将实现此接口,定义自动售货机在该状态下的行为方式。
-
- 实现具体状态类
每个状态类将实现 接口并为每个作定义其行为。MachineState
- 实现具体状态类
🟡 空闲状态
public class IdleState implements MachineState {
@Override
public void selectItem(VendingMachine context, String itemCode) {
System.out.println("Item selected: " + itemCode);
context.setSelectedItem(itemCode);
context.setState(new ItemSelectedState());
}
@Override
public void insertCoin(VendingMachine context, double amount) {
System.out.println("Please select an item before inserting coins.");
}
@Override
public void dispenseItem(VendingMachine context) {
System.out.println("No item selected. Nothing to dispense.");
}
}
🟠 ItemSelected状态
public class ItemSelectedState implements MachineState {
@Override
public void selectItem(VendingMachine context, String itemCode) {
System.out.println("Item already selected: " + context.getSelectedItem());
}
@Override
public void insertCoin(VendingMachine context, double amount) {
System.out.println("Inserted $" + amount + " for item: " + context.getSelectedItem());
context.setInsertedAmount(amount);
context.setState(new HasMoneyState());
}
@Override
public void dispenseItem(VendingMachine context) {
System.out.println("Insert coin before dispensing.");
}
}
🟢 有钱州
public class HasMoneyState implements MachineState {
@Override
public void selectItem(VendingMachine context, String itemCode) {
System.out.println("Cannot change item after inserting money.");
}
@Override
public void insertCoin(VendingMachine context, double amount) {
System.out.println("Money already inserted.");
}
@Override
public void dispenseItem(VendingMachine context) {
System.out.println("Dispensing item: " + context.getSelectedItem());
context.setState(new DispensingState());
// Simulate dispensing
try { Thread.sleep(1000); } catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
System.out.println("Item dispensed successfully.");
context.reset();
}
}
🔵 分配状态
public class DispensingState implements MachineState {
@Override
public void selectItem(VendingMachine context, String itemCode) {
System.out.println("Please wait, dispensing in progress.");
}
@Override
public void insertCoin(VendingMachine context, double amount) {
System.out.println("Please wait, dispensing in progress.");
}
@Override
public void dispenseItem(VendingMachine context) {
System.out.println("Already dispensing. Please wait.");
}
}
-
- 实现上下文 (VendingMachine)
类(我们的上下文)将维护对当前状态的引用,并将所有作委托给它。VendingMachine
public class VendingMachine {
private MachineState currentState;
private String selectedItem;
private double insertedAmount;public VendingMachine() { this.currentState = new IdleState(); // Initial state } public void setState(MachineState newState) { this.currentState = newState; } public void setSelectedItem(String itemCode) { this.selectedItem = itemCode; } public void setInsertedAmount(double amount) { this.insertedAmount = amount; } public String getSelectedItem() { return selectedItem; } public double getInsertedAmount() { return insertedAmount; } public void selectItem(String itemCode) { currentState.selectItem(this, itemCode); } public void insertCoin(double amount) { currentState.insertCoin(this, amount); } public void dispenseItem() { currentState.dispenseItem(this); } public void reset() { this.selectedItem = ""; this.insertedAmount = 0.0; this.currentState = new IdleState(); }
}
- 实现上下文 (VendingMachine)
客户端代码
public class VendingMachineApp {
public static void main(String[] args) {
VendingMachine vm = new VendingMachine();
vm.insertCoin(1.0); // Invalid in IdleState
vm.selectItem("A1");
vm.insertCoin(1.5);
vm.dispenseItem();
System.out.println("\n--- Second Transaction ---");
vm.selectItem("B2");
vm.insertCoin(2.0);
vm.dispenseItem();
}
}
通过使用状态模式,我们将僵化的、条件密集的实现转变为一个干净、灵活的架构,其中行为和转换被明确定义、解耦且易于维护。