状态模式(State Pattern)是一种行为型设计模式,它允许一个对象在其内部状态改变时改变它的行为。这种模式将状态-specific(特定于状态的)行为局部化到State类中,并通过组合的方式将制定的状态类委托给Context类。
状态模式主要包含以下几个角色:
使用一个简单的电视机例子来说明状态模式。
有一个简单的电视机,它有两个状态:开机和关机。在每个状态下,电视机对相同的操作会有不同的反应。
public interface TVState {
void pressOnOff(TV context);
void pressChannel(TV context);
void pressVolume(TV context);
}
这个接口定义了电视机可能接收到的所有操作。
// 关机状态
public class OffState implements TVState {
@Override
public void pressOnOff(TV context) {
System.out.println("电视机开机了");
context.setState(new OnState());
}
@Override
public void pressChannel(TV context) {
System.out.println("无效操作,电视机没有开机");
}
@Override
public void pressVolume(TV context) {
System.out.println("无效操作,电视机没有开机");
}
}
// 开机状态
public class OnState implements TVState {
@Override
public void pressOnOff(TV context) {
System.out.println("电视机关机了");
context.setState(new OffState());
}
@Override
public void pressChannel(TV context) {
System.out.println("切换到下一个频道");
}
@Override
public void pressVolume(TV context) {
System.out.println("音量增加");
}
}
这两个类实现了 TVState
接口,分别代表电视机的开机和关机状态。
public class TV {
private TVState state;
public TV() {
// 初始状态为关机
this.state = new OffState();
}
public void setState(TVState state) {
this.state = state;
}
public void pressOnOff() {
state.pressOnOff(this);
}
public void pressChannel() {
state.pressChannel(this);
}
public void pressVolume() {
state.pressVolume(this);
}
}
TV
类持有一个 TVState
对象,并将所有操作委托给当前状态对象。
public class Main {
public static void main(String[] args) {
TV tv = new TV();
// 电视机初始为关机状态
tv.pressChannel(); // 输出:无效操作,电视机没有开机
tv.pressVolume(); // 输出:无效操作,电视机没有开机
tv.pressOnOff(); // 输出:电视机开机了
// 现在电视机处于开机状态
tv.pressChannel(); // 输出:切换到下一个频道
tv.pressVolume(); // 输出:音量增加
tv.pressOnOff(); // 输出:电视机关机了
// 电视机又回到关机状态
tv.pressChannel(); // 输出:无效操作,电视机没有开机
}
}
3.5.1 状态接口 (TVState):
3.5.2 具体状态类 (OffState 和 OnState):
3.5.3 上下文类 (TV):
3.5.4 状态转换:
OffState
的 pressOnOff
方法中,状态被改变为 OnState
。3.5.5 客户端代码:
TV
对象并调用其方法。TV
对象交互。这个例子展示了状态模式的核心思想:
4.1 将与特定状态相关的行为局部化,并且将不同状态的行为分割开来。
4.2 使得状态转换显式化,减少对象间的相互依赖。
4.3 消除庞大的条件分支语句。
5.1 可能会导致状态类的数量过多。
5.2 增加了系统的复杂度。
6.1 一个对象的行为取决于它的状态,并且它必须在运行时刻根据状态改变它的行为。
6.2 一个操作中含有庞大的多分支的条件语句,且这些分支依赖于该对象的状态。
7.1 策略模式:状态模式可以被视为策略模式的扩展。主要区别在于,在状态模式中,特定的状态知道其他状态的存在并触发从一个状态到另一个状态的转换,而策略则是独立的、互不知道的算法。
7.2 单例模式:状态对象通常被实现为单例,因为一个Context对象在任何给定时刻只能拥有一个状态。
Iterator 接口是Java集合框架中最常见的状态模式实现之一。
import java.util.*;
public class IteratorExample {
public static void main(String[] args) {
List<String> list = new ArrayList<>(Arrays.asList("A", "B", "C"));
Iterator<String> iterator = list.iterator();
// hasNext() 和 next() 方法的行为取决于迭代器的当前状态
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
// 此时迭代器状态已经改变,再次调用 next() 会抛出 NoSuchElementException
try {
iterator.next();
} catch (NoSuchElementException e) {
System.out.println("Reached the end of the iterator");
}
}
}
在这个例子中,Iterator 的行为随着其内部状态(当前位置)的改变而改变。
InputStream 类及其子类使用了类似状态模式的设计。
import java.io.*;
public class InputStreamExample {
public static void main(String[] args) {
byte[] data = "Hello, World!".getBytes();
try (InputStream inputStream = new ByteArrayInputStream(data)) {
int byteRead;
while ((byteRead = inputStream.read()) != -1) {
System.out.print((char) byteRead);
}
// 此时流已经到达末尾,再次读取会返回 -1
System.out.println("\nEnd of stream: " + inputStream.read());
} catch (IOException e) {
e.printStackTrace();
}
}
}
在这个例子中,InputStream 的 read() 方法的行为取决于其内部状态(是否到达流的末尾)。
JavaServer Faces (JSF) 技术中的 Lifecycle 类使用了状态模式。
import javax.faces.lifecycle.Lifecycle;
import javax.faces.lifecycle.LifecycleFactory;
public class LifecycleExample {
public static void main(String[] args) {
LifecycleFactory lifecycleFactory = LifecycleFactory.getInstance();
Lifecycle lifecycle = lifecycleFactory.getLifecycle(LifecycleFactory.DEFAULT_LIFECYCLE);
// execute() 方法的行为取决于当前的生命周期阶段
lifecycle.execute(facesContext);
lifecycle.render(facesContext);
}
}
在 JSF 中,Lifecycle 的行为随着其所处的阶段(如 Restore View, Apply Request Values 等)而变化。
Thread 类虽然不是严格意义上的状态模式实现,但它的设计思想与状态模式相似。
public class ThreadStateExample {
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(() -> {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
System.out.println("New thread state: " + thread.getState());
thread.start();
System.out.println("After start(): " + thread.getState());
Thread.sleep(500);
System.out.println("During sleep(): " + thread.getState());
thread.join();
System.out.println("After join(): " + thread.getState());
}
}
在这个例子中,Thread 对象的行为和可用操作随着其状态(NEW, RUNNABLE, TIMED_WAITING, TERMINATED)的变化而变化。
状态模式提供了一种将对象的行为封装在不同状态对象中的方法。它可以消除条件判断语句,使得对象的结构更加清晰,同时也使得状态的切换更加明确。然而,使用状态模式也可能导致状态类的数量增多,增加系统的复杂度。因此,在使用状态模式时需要权衡其利弊,根据实际情况决定是否采用。