state

第二十二章 状态模式(State Pattern)

1 概要

状态模式(State Pattern)是一种行为模式(Behavior Design Patters)。它帮助开发人员设计一种能够根据内部状态变化而改变行为方式的系统。换句话说,这个系统的行为方式是由其内部状态决定的。在状态模式下,系统内部的各种状态和行为被封装在不同的类中,系统能够自动的根据内部状态,将行为执行的过程分派给对应的类。这也正是面向对象程序设计中多态特性的体现。状态模式非常适合实现状态机(State Machine)或者由状态驱动的系统。

2 状态模式的结构

在状态模式中有着3个参与方。

  1. Context用于表示整个系统运行的上下文。它定义了用户接口,根据系统当前的状态,将执行过程分派给具体的执行对象。
  2. State用于表示系统的内部状态接口。
  3. ConcreteState用于表示一个具体的状态,并且实现当前状态下应该执行的行为。

图一 状态模式结构

图一 状态模式结构。

3 状态示例

我们将使用自动贩卖机(Vending Machine)为例,讲解使用状态模式来实现自动贩卖机的状态迁移过程。

VendingMachineState表示的是自动贩卖机的状态接口。它有三个成员方法,分别表示投币、选择商品和分发商品三个操作。

public interface VendingMachineState {
    void insertCoin();  // 投币
    void pressButton(); // 选择商品
    void dispense();    // 分发商品
}

自动贩卖机的初始状态由NoCoinState类表示。它代表着尚未投币的状态。NoCoinState类中的成员machine指向自动贩卖机对象。

public class NoCoinState implements VendingMachineState {
    private CandyVendingMachine machine = null;
    
    public NoCoinState(CandyVendingMachine machine){
        this.machine = machine;
    }
    
    @Override
    public void insertCoin() { 
        // 已投币,转换到 ContainsCoinState 状态
        machine.setState(machine.getContainsCoinState());
    }
    
    @Override
    public void pressButton() {
        System.out.println("No coin inserted");
    }
    
    @Override
    public void dispense() {
        System.out.println("No coin inserted");
    }
}

ContainsCoinState类表示的是自动贩卖机在投币之后的状态。在这个状态下,用户可以选择商品。

public class ContainsCoinState implements CandyVendingMachineState{
    private CandyVendingMachine machine = null;
    
    public ContainsCoinState(CandyVendingMachine machine){
        this.machine = machine;
    }
    
    @Override
    public void insertCoin() {
        System.out.println("Coin already inserted");
    }
    @Override
    public void pressButton() {
        // 用户选择了商品,转换到分发商品状态
        machine.setState(machine.getDispensedState());
    }
    
    @Override
    public void dispense() {
        System.out.println("Press button to dispense");
    }
}

DispensedState类表示自动贩卖机分发商品的状态。在分发商品时,如果自动贩卖机还有剩余的商品的话,会分发给用户,回到初始状态。如果商品已售罄,则转移到NoCandyState状态。

public class DispensedState implements CandyVendingMachineState{
    CandyVendingMachine machine = null;
    
    public DispensedState(CandyVendingMachine machine){
        this.machine = machine;
    }
    
    @Override
    public void insertCoin() {
        System.out.println("Error. System is currently dispensing");
    }
    
    @Override
    public void pressButton() {
        System.out.println("Error. System is currently dispensing");
    }
    
    @Override
    public void dispense() {
        if(machine.getCount()>0) {
            // 分发商品,返回初始状态。
            machine.setState(machine.getNoCoinState());
            machine.setCount(machine.getCount()-1);
        }
        else{
            // 已售罄,转到 NoCandyState 状态
            System.out.println("No candies available");
            machine.setState(machine.getNoCandyState());
        }
    }
}

NoCandyState状态表示的是已售罄状态。当自动贩卖机已空,无法再继续出售商品时,会处于这个状态,等待工作人员添加新的商品。

public class NoCandyState implements CandyVendingMachineState {
    private CandyVendingMachine machine = null;
    
    public NoCandyState(CandyVendingMachine machine){
        this.machine = machine;
    }
    
    @Override
    public void insertCoin() {
        System.out.println("No candies available");
    }
    
    @Override
    public void pressButton() {
        System.out.println("No candies available");
    }
    
    @Override
    public void dispense() {
        System.out.println("No candies available");
    }
}

我们还定义了自动贩卖机CandyVendingMachine。它的内部存放了6个成员变量。成员变量count用于记录自动贩卖机中商品的个数。当count的值为0时,表示商品已售罄。成员变量state用于记录自动贩卖机当前处于的状态。其余的4个成员变量分别表示自动贩卖机的一种可能状态。

public class CandyVendingMachine {
    private int count;
    private CandyVendingMachineState state;

    private CandyVendingMachineState noCoinState;        // 初始状态
    private CandyVendingMachineState noCandyState;       // 商品已售罄状态
    private CandyVendingMachineState dispensedState;     // 分发商品状态
    private CandyVendingMachineState containsCoinState;  // 已投币状态
    

    public CandyVendingMachine(int numberOfCandies) {
        count = numberOfCandies;  // 设置商品数量
        state = noCoinState;      // 设置初始状态

        noCoinState = new NoCoinState(this);
        noCandyState = new NoCandyState(this);
        dispensedState = new DispensedState(this);
        containsCoinState = new ContainsCoinState(this);
    }

    public void insertCoin(){
        System.out.println("You inserted a coin.");
        state.insertCoin();
    }

    public void pressButton(){
        System.out.println("You have pressed the button.");
        state.pressButton();
        state.dispense();
    }

    // Getters and Setters
    public int getCount() {
        return count;
    }

    public void setCount(int count) {
        this.count = count;
    }

    public CandyVendingMachineState getState() {
        return state;
    }

    public CandyVendingMachineState getNoCandyState() {
        return noCandyState;
    }

    public CandyVendingMachineState getNoCoinState() {
        return noCoinState;
    }

    public void setState(CandyVendingMachineState state) {
        this.state = state;
    }

    public CandyVendingMachineState getContainsCoinState() {
        return containsCoinState;
    }

    public CandyVendingMachineState getDispensedState() {
        return dispensedState;
    }
}

最后,我们来看看如果使用自动贩卖机。首先,我们创建一个CandyVendingMachine对象。在初始状态下,它有3个商品。我们分别投币购买商品四次。最后,该自动贩卖机会停在"已售罄"状态,即NoCandyState状态。

public class VendingMachineExample {
    public static void main(String[] args) {
        CandyVendingMachine machine = new CandyVendingMachine(3);

        // 购买第一件商品
        machine.insertCoin();
        machine.pressButton();

        // 购买第二件商品
        machine.insertCoin();
        machine.pressButton();

        // 购买第三件商品
        machine.insertCoin();
        machine.pressButton();

        // 已售罄,无法购买
        machine.insertCoin();
        machine.pressButton();
    }
}

4 小结

本章介绍了状态模式的结构和使用方法。状态模式将状态信息封装在状态类中。通过状态迁移,将对象从一个状态迁移至另一个状态。因为与状态相关的处理逻辑也封装在状态对象中,这使得对象在某一状态下,只能完成相应的逻辑处理。因为状态之间是相互独立的,因此,使用状态模式能够帮助开发人员更加清晰的组织代码,减轻代码维护的负担。

上一章
下一章

注册用户登陆后可留言

Copyright  2019 Little Waterdrop, LLC. All Rights Reserved.