【HeadFirst系列之HeadFirst设计模式】第12天之状态模式:让对象的行为随状态而变化!

Scroll Down

状态模式:让对象的行为随状态而变化!

大家好!今天我们来聊聊设计模式中的状态模式(State Pattern)。如果你曾经遇到过对象的行为需要根据其状态而变化的情况,那么状态模式就是你的救星!本文基于《Head First 设计模式》的状态模式章节,通过生动的故事和 Java 代码示例,带你轻松掌握状态模式的精髓。我们还会探讨状态模式在 JDKSpring 等框架中的应用,让你真正理解它的实际价值。

11-设计模式之状态模式-事物的状态-前言


1. 状态模式是什么?

状态模式是一种行为型设计模式,它允许对象在其内部状态改变时改变其行为。状态模式的核心思想是将对象的状态封装成独立的类,并将对象的行为委托给当前状态对象。

适用场景

  • 对象的行为依赖于它的状态,并且需要在运行时根据状态改变行为。
  • 代码中包含大量与状态相关的条件语句,希望通过状态模式消除这些条件分支。

2. 状态模式的实现

故事背景

小明开发了一个糖果机系统,糖果机有几种状态:

  1. 没有糖果(No Coin)
  2. 有糖果但未投币(Has Coin)
  3. 已投币(Coin Inserted)
  4. 售出糖果(Sold)

糖果机的行为(如投币、退币、转动曲柄、发放糖果)会根据当前状态而变化。

问题出现

如果直接在糖果机类中使用大量的 if-elseswitch-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 线程的生命周期中有多种状态(如 NEWRUNNABLEBLOCKED 等),线程的行为会根据状态而变化。
  • AWT/Swing 组件状态:GUI 组件(如按钮、文本框)的行为会根据其状态(如启用、禁用)而变化。

2. Spring 中的应用

  • 状态机(State Machine):Spring Statemachine 是一个基于状态模式的框架,用于实现复杂的状态管理逻辑。
  • 事务状态:Spring 的事务管理器会根据事务的状态(如开始、提交、回滚)执行不同的行为。

5. 总结

状态模式通过将对象的状态封装成独立的类,使得对象的行为可以随状态而变化。它的核心优势在于:

  • 消除条件分支:通过多态代替大量的 if-elseswitch-case 语句。
  • 提高扩展性:新增状态时只需添加新的状态类,无需修改现有代码。

希望本文能帮助你更好地理解状态模式,并在实际项目中灵活运用!


互动话题
你在项目中使用过状态模式吗?遇到过哪些有趣的问题?欢迎在评论区分享你的经验!