Java设计模式之状态模式详解

发布于:2025-06-08 ⋅ 阅读:(20) ⋅ 点赞:(0)

Java设计模式之状态模式详解

在软件开发过程中,我们经常会遇到这样的情况:一个对象的行为会根据其内部状态的变化而变化。例如,电梯可能处于运行、停止、维修等不同状态,不同状态下对按钮操作的响应不同;游戏角色在正常、受伤、死亡等状态下,其动作和交互方式也有差异。对于这类问题,状态模式(State Pattern)提供了优雅的解决方案。下面将通过图文结合的方式,详细介绍Java中的状态模式。

一、状态模式概念

状态模式是一种行为型设计模式,它允许一个对象在其内部状态改变时改变它的行为,使对象看起来似乎修改了它的类。在状态模式中,将对象的每个状态都封装成一个独立的类,这些类实现同一个状态接口,而拥有状态的对象仅需在内部维护一个当前状态对象的引用。当对象的状态发生变化时,只需要替换当前的状态对象,对象的行为就会随之改变,从而避免了大量的条件判断语句,使代码更加清晰、易于维护和扩展。

二、状态模式结构(Mermaid类图)

使用Mermaid绘制状态模式的类图,能直观呈现各角色关系。

1
1
Context
-state: State
+request()
+setState(State state)
State
+handle(Context context)
ConcreteStateA
+handle(Context context)
ConcreteStateB
+handle(Context context)

在上述类图中:

  • Context(上下文):维护一个对State对象的引用,代表当前状态,同时提供对外的操作接口,这些操作会委托给当前的State对象来处理。
  • State(状态接口):定义了一个接口,用于封装与Context的一个状态相关的行为,所有具体状态类都要实现这个接口。
  • ConcreteState(具体状态类):实现State接口,每个具体状态类都对应Context的一个具体状态,在具体状态类中实现与该状态相关的行为逻辑。

三、Java代码示例

以自动售货机为例,售货机有“有商品”“无商品”“售出商品”等状态,不同状态下投币、退币、购买商品的操作逻辑不同。下面通过Java代码实现这个自动售货机的状态模式。

1. 定义状态接口State

public interface State {
    void insertCoin();
    void ejectCoin();
    void turnCrank();
    void dispense();
}

2. 实现具体状态类

NoQuarterState(无硬币状态)

public class NoQuarterState implements State {
    private VendingMachine vendingMachine;

    public NoQuarterState(VendingMachine vendingMachine) {
        this.vendingMachine = vendingMachine;
    }

    @Override
    public void insertCoin() {
        System.out.println("You inserted a quarter");
        vendingMachine.setState(vendingMachine.getHasQuarterState());
    }

    @Override
    public void ejectCoin() {
        System.out.println("You haven't inserted a quarter yet");
    }

    @Override
    public void turnCrank() {
        System.out.println("You need to insert a quarter first");
    }

    @Override
    public void dispense() {
        System.out.println("You need to pay first");
    }
}

HasQuarterState(有硬币状态)

public class HasQuarterState implements State {
    private VendingMachine vendingMachine;

    public HasQuarterState(VendingMachine vendingMachine) {
        this.vendingMachine = vendingMachine;
    }

    @Override
    public void insertCoin() {
        System.out.println("You already inserted a quarter");
    }

    @Override
    public void ejectCoin() {
        System.out.println("Quarter returned");
        vendingMachine.setState(vendingMachine.getNoQuarterState());
    }

    @Override
    public void turnCrank() {
        System.out.println("You turned...");
        vendingMachine.setState(vendingMachine.getSoldState());
        vendingMachine.dispense();
    }

    @Override
    public void dispense() {
        System.out.println("No item dispensed");
    }
}

SoldState(售出商品状态)

public class SoldState implements State {
    private VendingMachine vendingMachine;

    public SoldState(VendingMachine vendingMachine) {
        this.vendingMachine = vendingMachine;
    }

    @Override
    public void insertCoin() {
        System.out.println("Please wait, we're already giving you an item");
    }

    @Override
    public void ejectCoin() {
        System.out.println("Sorry, you've already turned the crank");
    }

    @Override
    public void turnCrank() {
        System.out.println("Turning twice doesn't get you another item!");
    }

    @Override
    public void dispense() {
        vendingMachine.releaseItem();
        if (vendingMachine.getCount() > 0) {
            vendingMachine.setState(vendingMachine.getNoQuarterState());
        } else {
            System.out.println("Oops, out of stock!");
            vendingMachine.setState(vendingMachine.getOutOfStockState());
        }
    }
}

OutOfStockState(无商品状态)

public class OutOfStockState implements State {
    private VendingMachine vendingMachine;

    public OutOfStockState(VendingMachine vendingMachine) {
        this.vendingMachine = vendingMachine;
    }

    @Override
    public void insertCoin() {
        System.out.println("You can't insert a quarter, the machine is out of stock");
    }

    @Override
    public void ejectCoin() {
        System.out.println("You can't eject, you haven't inserted a quarter yet");
    }

    @Override
    public void turnCrank() {
        System.out.println("You turned, but there are no items");
    }

    @Override
    public void dispense() {
        System.out.println("No item to dispense");
    }
}

3. 定义上下文类VendingMachine

public class VendingMachine {
    State noQuarterState;
    State hasQuarterState;
    State soldState;
    State outOfStockState;

    State state = noQuarterState;
    int count = 0;

    public VendingMachine(int count) {
        this.count = count;
        noQuarterState = new NoQuarterState(this);
        hasQuarterState = new HasQuarterState(this);
        soldState = new SoldState(this);
        outOfStockState = new OutOfStockState(this);
    }

    public void insertCoin() {
        state.insertCoin();
    }

    public void ejectCoin() {
        state.ejectCoin();
    }

    public void turnCrank() {
        state.turnCrank();
        state.dispense();
    }

    void setState(State state) {
        this.state = state;
    }

    void releaseItem() {
        System.out.println("A gumball comes rolling out the slot...");
        if (count > 0) {
            count = count - 1;
        }
    }

    public State getNoQuarterState() {
        return noQuarterState;
    }

    public State getHasQuarterState() {
        return hasQuarterState;
    }

    public State getSoldState() {
        return soldState;
    }

    public State getOutOfStockState() {
        return outOfStockState;
    }

    public int getCount() {
        return count;
    }
}

4. 测试代码

public class VendingMachineTest {
    public static void main(String[] args) {
        VendingMachine vendingMachine = new VendingMachine(10);

        vendingMachine.insertCoin();
        vendingMachine.turnCrank();
        vendingMachine.ejectCoin();
        vendingMachine.insertCoin();
        vendingMachine.insertCoin();
        vendingMachine.turnCrank();

        for (int i = 0; i < 11; i++) {
            vendingMachine.turnCrank();
        }
    }
}

在上述代码中:

  • State接口定义了自动售货机在不同状态下可执行的操作方法。
  • 各个具体状态类(如NoQuarterStateHasQuarterState等)实现State接口,在其中编写对应状态下的操作逻辑,并在适当时候切换售货机的状态。
  • VendingMachine类作为上下文,持有所有具体状态类的实例,对外提供操作接口,将操作委托给当前状态对象处理,同时可以根据业务逻辑切换状态。
  • VendingMachineTest类用于测试自动售货机在不同操作下的状态变化和行为表现。

四、状态模式的优缺点

优点

  1. 清晰的逻辑分离:将不同状态下的行为逻辑封装在独立的状态类中,避免了大量的条件判断语句(如if-elseswitch-case),使代码结构更加清晰,易于理解和维护。
  2. 扩展性强:当需要增加新的状态时,只需要新增一个具体状态类并实现状态接口即可,不需要修改上下文类和其他状态类的代码,符合开闭原则。
  3. 可维护性高:由于每个状态类都专注于处理一种状态的行为,修改某个状态的行为逻辑时,只需要修改对应的状态类,不会影响其他状态的逻辑,降低了代码的耦合度。

缺点

  1. 类数量增加:每个具体状态都需要一个独立的类来实现,当状态较多时,会导致类的数量急剧增加,增加了系统的复杂性和维护成本。
  2. 状态转换的复杂性:在某些情况下,状态之间的转换规则可能比较复杂,需要仔细处理状态之间的转换逻辑,否则可能会出现状态混乱的问题。

五、应用场景

  1. 工作流系统:在工作流系统中,任务可能处于不同的状态,如“待处理”“处理中”“已完成”“已驳回”等,不同状态下对任务的操作(如提交、审批、退回等)逻辑不同,使用状态模式可以很好地管理这些状态和对应的操作。
  2. 游戏开发:游戏角色、游戏场景等往往存在多种状态,例如游戏角色的攻击、防御、跳跃、奔跑等状态,每个状态下的行为和动画效果不同,状态模式可以有效组织这些状态相关的逻辑。
  3. 设备状态管理:像打印机、路由器等设备,会有“就绪”“打印中”“故障”“离线”等状态,不同状态下对设备的操作响应不同,通过状态模式可以清晰地实现设备的状态管理和行为控制。

网站公告

今日签到

点亮在社区的每一天
去签到