当前位置: > > > Java - GoF设计模式详解21(状态模式)

Java - GoF设计模式详解21(状态模式)

二十一、状态模式

1,基本介绍

(1)状态模式(State)允许一个对象在其内部状态改变的时候改变它的行为,这个对象看起来像是改变了其类。
(2)该模式中包含的角色及其职责如下:
  • 环境角色Context):也叫作上下文,它定义了客户需要的接口,内部维护一个当前的状态,并负责具体状态的切换。
  • 抽象状态角色Abstract State):定义了一个接口,用以封装环境对象中的特定状态所对应的行为。
  • 具体状态Concrete State):实现抽象状态所定义的行为,并且在需要的情况下进行状态切换。

2,使用样例

(1)下面是一个关于打印机的状态模式示例。首先我们一个打印机状态接口(抽象状态),里面声明一个 print() 方法:
// 打印机状态接口
interface State {
  // 开始打印
  void print();
}

(2)接着定义了两种具体状态:可以打印的状态和缺纸的状态。
  • 当打印机处于可以打印的状态时,调用 print() 方法会打印“正在打印...”,并将打印机的状态切换到缺纸的状态。
  • 当打印机处于缺纸的状态时,调用 print() 方法会打印“缺纸,无法打印。”。
// 可以打印的状态
class CanPrintState implements State {
  // 打印机对象
  private Printer printer;
  public CanPrintState(Printer printer) {
    this.printer = printer;
  }

  @Override
  public void print() {
    System.out.println("正在打印...");
    // 切换状态
    printer.setState(printer.getOutOfPaperState());
  }
}


// 缺纸的状态
class OutOfPaperState implements State {
  // 打印机对象
  private Printer printer;
  public OutOfPaperState(Printer printer) {
    this.printer = printer;
  }

  @Override
  public void print() {
    System.out.println("缺纸,无法打印。");
  }
}

(3)然后定义一个打印机类(环境角色),内部维护一个当前的状态,并负责具体状态的切换。
// 打印机
class Printer {
  // 可以打印的状态
  private State canPrintState;
  // 缺纸的状态
  private State outOfPaperState;
  // 当前的状态
  private State state;

  public Printer() {
    // 初始化状态
    canPrintState = new CanPrintState(this);
    outOfPaperState = new OutOfPaperState(this);
    // 设置当前状态为可以打印的状态
    state = canPrintState;
  }

  // 打印
  public void print() {
    state.print();
  }

  // 设置状态
  public void setState(State state) {
    this.state = state;
  }

  public State getCanPrintState() {
    return canPrintState;
  }

  public State getOutOfPaperState() {
    return outOfPaperState;
  }
}

(4)最后测试一下,我们们创建了一个 Printer 对象,并调用了它的 print() 方法。由于打印机的初始状态是可以打印的,所以在第一次调用 print() 方法时会打印“正在打印...”,并将打印机的状态切换到缺纸的状态。接着,当我们再次调用 print() 方法时,由于打印机的状态已经是缺纸,所以会打印“缺纸,无法打印。”。
public class Test {
  public static void main(String[] args) {
    Printer printer = new Printer();
    printer.print();
    printer.print();
  }
}

附一:JDK 中的状态模式

(1)在 JDK 中的 java.util.Iterator 接口使用了状态模式。java.util.Iterator 中 的 hasNextnext 方法就是根据迭代器的内部状态来决定如何表现的。
  • 例如,当迭代器处于“已遍历完集合中的所有元素”的状态时,调用 next 方法将抛出一个 NoSuchElementException 异常。

(2)java.util.Iterator 是一个接口,它包含了两个方法:hasNextnext。这两个方法的实现是由迭代器的具体实现类来决定的。这些具体实现类可以是某个集合的内部类,也可以是一个单独的类。
  • hasNext 方法返回一个布尔值,表示迭代器是否还有下一个元素可供遍历。
  • next 方法返回迭代器的下一个元素。

(3)在这些具体实现类中,迭代器会维护一些内部状态,比如当前遍历到的位置、是否已遍历完集合中的所有元素等。根据这些状态,迭代器的 hasNextnext 方法就会做出不同的反应。
例如,在 java.util.ArrayList 的内部类 Itr 中,迭代器维护了一个 cursor 变量,表示当前遍历到的位置。
  • 在调用 hasNext 方法时,如果 cursor 小于 size(集合的大小),则说明还有下一个元素可供遍历,因此返回 true;如果 cursor 等于 size,则说明已遍历完集合中的所有元素,因此返回 false
  • 在调用 next 方法时,如果 cursor 小于 size,则返回下一个元素,并将 cursor 加一;如果 cursor 等于 size,则说明已遍历完集合中的所有元素,此时调用 next 方法就会抛出一个 NoSuchElementException 异常。

(4)总之,Iterator 中的状态模式的角色如下所示:
  • Context(上下文):这里的 Context 就是迭代器本身,也就是具体实现类。它维护了一些内部状态,并提供了 hasNextnext 方法来遍历集合中的元素。
  • State(状态):在这个示例中,状态就是迭代器的内部状态,比如当前遍历到的位置、是否已遍历完集合中的所有元素等。
  • ConcreteState(具体状态):在这个示例中,就是迭代器的两种内部状态:“有下一个元素可供遍历”和“已遍历完集合中的所有元素”。

附二:Spring 中的状态模式

1,Spring Statemachine

(1)Spring Statemachine 是一个基于状态模式的状态机框架。它提供了一种简单而灵活的方法来管理状态,转移和事件。

(2)在 Spring Statemachine 中,状态机的状态和转移是由状态实例和转移实例表示的。状态机的行为由状态实例和转移实例之间的关系定义,而不是由状态机的实现细节定义。这使得状态机的行为可以在运行时更改,并且可以在不同的场景中重用。
关于 Spring Statemachine 更详细的介绍可以参考我之前写的文章:

2,Spring Batch

    在 Spring Batch 中,批处理作业有多种状态,如已启动、已停止、已完成等。Spring Batch 通过使用状态模式来管理批处理作业的状态,使得批处理作业的管理更加灵活,可控。例如,在启动作业之前,作业处于停止状态,启动作业时,作业状态会变为运行状态。
关于 Spring Batch 更详细的介绍可以参考我之前写的文章:
评论0