引言
“人生如戏,全靠状态。”——设计模式也深知这一点。
一、什么是状态模式(State Pattern)
状态模式是一种行为型设计模式,允许对象在内部状态改变时,改变它的行为。也就是说,看起来像是修改了它的类。
通俗解释
就像一只电风扇,有关机状态、低速状态、高速状态。你按下按钮,它就在不同的状态间切换。状态决定了电风扇的行为。
二、示例讲解:
本篇我们以“自动售货机 Vending Machine”为例。它可能处于以下状态:
- 等待投币
- 已投币,等待选择
- 正在出货
- 售罄
用户的操作行为(如投币、选择商品、取货)会根据当前状态产生不同的响应。
三、UML 图结构(PlantUML):
@startuml
interface State {
+insertCoin()
+selectProduct()
+dispense()
}
class WaitingForCoin implements State
class HasCoin implements State
class Dispensing implements State
class SoldOut implements State
class VendingMachine {
-state: State
+setState(s: State)
+insertCoin()
+selectProduct()
+dispense()
}
State <|.. WaitingForCoin
State <|.. HasCoin
State <|.. Dispensing
State <|.. SoldOut
VendingMachine --> State
@enduml
四、Java 实现代码
状态接口
public interface State {
void insertCoin();
void selectProduct();
void dispense();
}
具体状态实现
等待投币状态
public class WaitingForCoinState implements State {
private VendingMachine machine;
public WaitingForCoinState(VendingMachine machine) {
this.machine = machine;
}
@Override
public void insertCoin() {
System.out.println("硬币已投入。");
machine.setState(machine.getHasCoinState());
}
@Override
public void selectProduct() {
System.out.println("请先投币!");
}
@Override
public void dispense() {
System.out.println("不能发货,请先投币并选择商品。");
}
}
已投币状态
public class HasCoinState implements State {
private VendingMachine machine;
public HasCoinState(VendingMachine machine) {
this.machine = machine;
}
@Override
public void insertCoin() {
System.out.println("已经有硬币了,请先选择商品。");
}
@Override
public void selectProduct() {
System.out.println("商品已选择,正在出货...");
machine.setState(machine.getDispensingState());
}
@Override
public void dispense() {
System.out.println("请先选择商品!");
}
}
出货中状态
public class DispensingState implements State {
private VendingMachine machine;
public DispensingState(VendingMachine machine) {
this.machine = machine;
}
@Override
public void insertCoin() {
System.out.println("正在出货,请稍候...");
}
@Override
public void selectProduct() {
System.out.println("已经选择了,请等待出货。");
}
@Override
public void dispense() {
System.out.println("出货完成!");
if (machine.getProductCount() > 0) {
machine.setState(machine.getWaitingForCoinState());
} else {
System.out.println("商品售罄!");
machine.setState(machine.getSoldOutState());
}
}
}
售罄状态
public class SoldOutState implements State {
private VendingMachine machine;
public SoldOutState(VendingMachine machine) {
this.machine = machine;
}
@Override
public void insertCoin() {
System.out.println("商品已售罄,请勿投币。");
}
@Override
public void selectProduct() {
System.out.println("无法选择商品,已售罄。");
}
@Override
public void dispense() {
System.out.println("无法出货,商品售罄。");
}
}
状态持有者:VendingMachine 类
public class VendingMachine {
private State waitingForCoinState;
private State hasCoinState;
private State dispensingState;
private State soldOutState;
private State currentState;
private int productCount;
public VendingMachine(int initialCount) {
waitingForCoinState = new WaitingForCoinState(this);
hasCoinState = new HasCoinState(this);
dispensingState = new DispensingState(this);
soldOutState = new SoldOutState(this);
this.productCount = initialCount;
this.currentState = initialCount > 0 ? waitingForCoinState : soldOutState;
}
public void insertCoin() {
currentState.insertCoin();
}
public void selectProduct() {
currentState.selectProduct();
}
public void dispense() {
currentState.dispense();
if (productCount > 0 && currentState == dispensingState) {
productCount--;
}
}
// Getters & Setters
public void setState(State state) {
this.currentState = state;
}
public State getWaitingForCoinState() { return waitingForCoinState; }
public State getHasCoinState() { return hasCoinState; }
public State getDispensingState() { return dispensingState; }
public State getSoldOutState() { return soldOutState; }
public int getProductCount() { return productCount; }
}
测试代码
public class Main {
public static void main(String[] args) {
VendingMachine machine = new VendingMachine(2);
machine.insertCoin();
machine.selectProduct();
machine.dispense();
machine.insertCoin();
machine.selectProduct();
machine.dispense();
// 尝试再买一次
machine.insertCoin();
machine.selectProduct();
machine.dispense();
}
}
五、总结:状态模式的优缺点
优点:
- 封装状态转换逻辑,消除了大量 if-else
- 新状态扩展易如反掌
- 状态切换透明,对外接口不变
缺点:
- 类数量变多(每个状态一个类)
- 状态之间可能耦合紧密
六、适用场景
- 对象行为依赖于状态变化(如:文档编辑器、交通灯、媒体播放器)
- 状态可枚举、行为可切换,但不适合用大量 if/else 处理