状态模式:让对象的行为随状态而变化!
大家好!今天我们来聊聊设计模式中的状态模式(State Pattern)。如果你曾经遇到过对象的行为需要根据其状态而变化的情况,那么状态模式就是你的救星!本文基于《Head First 设计模式》的状态模式章节,通过生动的故事和 Java 代码示例,带你轻松掌握状态模式的精髓。我们还会探讨状态模式在 JDK 和 Spring 等框架中的应用,让你真正理解它的实际价值。
1. 状态模式是什么?
状态模式是一种行为型设计模式,它允许对象在其内部状态改变时改变其行为。状态模式的核心思想是将对象的状态封装成独立的类,并将对象的行为委托给当前状态对象。
适用场景
- 对象的行为依赖于它的状态,并且需要在运行时根据状态改变行为。
- 代码中包含大量与状态相关的条件语句,希望通过状态模式消除这些条件分支。
2. 状态模式的实现
故事背景
小明开发了一个糖果机系统,糖果机有几种状态:
- 没有糖果(No Coin)
- 有糖果但未投币(Has Coin)
- 已投币(Coin Inserted)
- 售出糖果(Sold)
糖果机的行为(如投币、退币、转动曲柄、发放糖果)会根据当前状态而变化。
问题出现
如果直接在糖果机类中使用大量的 if-else
或 switch-case
语句来处理不同状态的行为,代码会变得难以维护和扩展。
解决方案:状态模式
小明决定使用状态模式,将每个状态封装成一个独立的类,并将糖果机的行为委托给当前状态对象。
3. 代码实现
1. 定义状态接口
首先,我们定义一个状态接口,包含糖果机的所有行为。
// 状态接口
interface State {
void insertCoin();
void ejectCoin();
void turnCrank();
void dispense();
}
2. 实现具体状态类
接下来,我们为每个状态实现具体的状态类。
// 没有糖果的状态
class NoCandyState implements State {
private CandyMachine machine;
public NoCandyState(CandyMachine machine) {
this.machine = machine;
}
@Override
public void insertCoin() {
System.out.println("No candy available. Coin returned.");
}
@Override
public void ejectCoin() {
System.out.println("No coin to eject.");
}
@Override
public void turnCrank() {
System.out.println("No candy available.");
}
@Override
public void dispense() {
System.out.println("No candy available.");
}
}
// 有糖果但未投币的状态
class HasCandyState implements State {
private CandyMachine machine;
public HasCandyState(CandyMachine machine) {
this.machine = machine;
}
@Override
public void insertCoin() {
System.out.println("Coin inserted.");
machine.setState(machine.getCoinInsertedState());
}
@Override
public void ejectCoin() {
System.out.println("No coin to eject.");
}
@Override
public void turnCrank() {
System.out.println("Please insert a coin first.");
}
@Override
public void dispense() {
System.out.println("Please insert a coin first.");
}
}
// 已投币的状态
class CoinInsertedState implements State {
private CandyMachine machine;
public CoinInsertedState(CandyMachine machine) {
this.machine = machine;
}
@Override
public void insertCoin() {
System.out.println("Coin already inserted.");
}
@Override
public void ejectCoin() {
System.out.println("Coin ejected.");
machine.setState(machine.getHasCandyState());
}
@Override
public void turnCrank() {
System.out.println("Crank turned. Dispensing candy.");
machine.setState(machine.getSoldState());
}
@Override
public void dispense() {
System.out.println("Please turn the crank.");
}
}
// 售出糖果的状态
class SoldState implements State {
private CandyMachine machine;
public SoldState(CandyMachine machine) {
this.machine = machine;
}
@Override
public void insertCoin() {
System.out.println("Please wait, dispensing candy.");
}
@Override
public void ejectCoin() {
System.out.println("Coin already used.");
}
@Override
public void turnCrank() {
System.out.println("Crank already turned.");
}
@Override
public void dispense() {
System.out.println("Candy dispensed.");
machine.releaseCandy();
if (machine.getCandyCount() > 0) {
machine.setState(machine.getHasCandyState());
} else {
machine.setState(machine.getNoCandyState());
}
}
}
3. 实现糖果机类
糖果机类持有所有状态对象,并将行为委托给当前状态对象。
// 糖果机类
class CandyMachine {
private State noCandyState;
private State hasCandyState;
private State coinInsertedState;
private State soldState;
private State currentState;
private int candyCount;
public CandyMachine(int candyCount) {
this.candyCount = candyCount;
noCandyState = new NoCandyState(this);
hasCandyState = new HasCandyState(this);
coinInsertedState = new CoinInsertedState(this);
soldState = new SoldState(this);
if (candyCount > 0) {
currentState = hasCandyState;
} else {
currentState = noCandyState;
}
}
public void insertCoin() {
currentState.insertCoin();
}
public void ejectCoin() {
currentState.ejectCoin();
}
public void turnCrank() {
currentState.turnCrank();
currentState.dispense();
}
public void setState(State state) {
this.currentState = state;
}
public void releaseCandy() {
if (candyCount > 0) {
candyCount--;
}
}
public State getNoCandyState() {
return noCandyState;
}
public State getHasCandyState() {
return hasCandyState;
}
public State getCoinInsertedState() {
return coinInsertedState;
}
public State getSoldState() {
return soldState;
}
public int getCandyCount() {
return candyCount;
}
}
4. 客户端代码
public class CandyMachineTest {
public static void main(String[] args) {
CandyMachine machine = new CandyMachine(2);
machine.insertCoin(); // 输出: Coin inserted.
machine.turnCrank(); // 输出: Crank turned. Dispensing candy. Candy dispensed.
machine.insertCoin(); // 输出: Coin inserted.
machine.ejectCoin(); // 输出: Coin ejected.
machine.insertCoin(); // 输出: Coin inserted.
machine.turnCrank(); // 输出: Crank turned. Dispensing candy. Candy dispensed.
machine.insertCoin(); // 输出: No candy available. Coin returned.
}
}
4. 状态模式在 JDK 和 Spring 中的应用
1. JDK 中的应用
- 线程状态:Java 线程的生命周期中有多种状态(如
NEW
、RUNNABLE
、BLOCKED
等),线程的行为会根据状态而变化。 - AWT/Swing 组件状态:GUI 组件(如按钮、文本框)的行为会根据其状态(如启用、禁用)而变化。
2. Spring 中的应用
- 状态机(State Machine):Spring Statemachine 是一个基于状态模式的框架,用于实现复杂的状态管理逻辑。
- 事务状态:Spring 的事务管理器会根据事务的状态(如开始、提交、回滚)执行不同的行为。
5. 总结
状态模式通过将对象的状态封装成独立的类,使得对象的行为可以随状态而变化。它的核心优势在于:
- 消除条件分支:通过多态代替大量的
if-else
或switch-case
语句。 - 提高扩展性:新增状态时只需添加新的状态类,无需修改现有代码。
希望本文能帮助你更好地理解状态模式,并在实际项目中灵活运用!
互动话题
你在项目中使用过状态模式吗?遇到过哪些有趣的问题?欢迎在评论区分享你的经验!