状态模式
本课属于 Coddy Java 学习旅程的“面向对象编程”单元。第 74 课,共 87 课。
状态模式是一种行为型设计模式,它允许对象在内部状态发生变化时改变行为。对象看起来像是改变了自己的类,但实际上,它将行为委托给不同的状态对象。想象一台自动售货机:它对按下按钮的响应取决于它是处于空闲状态、已投入钱币,还是正在出货。
你不再使用复杂的条件语句来处理不同状态,而是将每种状态封装在自己的类中。该模式包含一个用于保存当前状态引用的Context,以及为每种状态定义行为的State类:
interface State {
void handle(Context context);
}
class Context {
private State state;
public void setState(State state) {
this.state = state;
}
public void request() {
state.handle(this);
}
}每个具体状态都会实现相应的行为,并可以触发转换到其他状态:
class IdleState implements State {
public void handle(Context context) {
System.out.println("Idle: Insert coin to start");
context.setState(new ActiveState());
}
}
class ActiveState implements State {
public void handle(Context context) {
System.out.println("Active: Processing...");
context.setState(new IdleState());
}
}上下文会将请求委托给当前状态,并随着状态的变化自动改变行为:
Context context = new Context();
context.setState(new IdleState());
context.request(); // 空闲:投入硬币即可开始
context.request(); // 活动中:正在处理……当对象的行为高度依赖其状态,并且必须在运行时改变时,状态模式是理想的选择。它能消除大型条件代码块,让添加新状态变得简单,只需创建一个实现 State 接口的新类即可。
挑战
简单让我们使用状态模式构建一个文档工作流系统!你将创建一个系统,用来模拟文档如何经历不同阶段:Draft、Review 和 Published。每种状态都会决定可以执行哪些操作,以及尝试编辑或批准文档时文档会如何表现。
你将把代码分到四个文件中:
DocumentState.java:定义所有状态类都要实现的DocumentState接口。它应声明两个方法:edit(Document doc)用于尝试编辑文档,approve(Document doc)用于尝试批准文档或将文档推进到下一阶段。States.java:创建三个实现DocumentState的具体状态类:DraftState:调用edit时,打印Editing draft...。调用approve时,打印Draft approved. Moving to review.,并将文档转换为ReviewState。ReviewState:调用edit时,打印Cannot edit during review.(状态不变)。调用approve时,打印Review complete. Publishing document.,并转换为PublishedState。PublishedState:调用edit时,打印Cannot edit published document.。调用approve时,打印Document already published.。这两个操作都不会改变状态。Document.java:创建用于维护当前状态的上下文类。你的Document应包含:一个保存当前
DocumentState的私有字段,并在构造函数中将其初始化为DraftState。一个用于更改当前状态的
setState(DocumentState state)方法。一个将调用委托给当前状态的
edit方法的edit()方法。一个将调用委托给当前状态的
approve方法的approve()方法。一个返回当前状态的简单类名的
getStatus()方法(使用getClass().getSimpleName())。Main.java:演示你的状态系统!你会收到一个输入:由逗号分隔的操作字符串,其中每个操作都是edit或approve(例如:edit,approve,edit,approve)。创建一个
Document,并按Status: [state]的格式打印它的初始状态。然后按顺序处理每个操作,在文档上调用相应的方法。每个操作执行后,打印当前状态。
你会收到一个输入:由逗号分隔的操作字符串。
例如,输入为 edit,approve,approve 时,输出为:
Status: DraftState
Editing draft...
Status: DraftState
Draft approved. Moving to review.
Status: ReviewState
Review complete. Publishing document.
Status: PublishedState请注意,同一个 approve() 调用会根据文档的当前状态产生不同结果。这正是状态模式的核心!文档不会使用条件语句来检查自身状态,而是将行为委托给当前状态对象,由它恰当地处理操作,并在需要时触发状态转换。
亲手试试
import java.util.Scanner;
class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
String input = scanner.nextLine();
// TODO:创建一个新的 Document 对象
// TODO:按 "Status: [state]" 格式输出初始状态
// TODO:用逗号分割输入,得到各个操作
// TODO:循环处理每个操作
// 如果操作是 "edit",就对 document 调用 edit()
// 如果操作是 "approve",就对 document 调用 approve()
// 每次操作后,输出当前状态
}
}
这节课包含一个小测验。开始学习即可作答,并记录你的进度。
面向对象编程中的所有课
自己练习: Java 在线编译器