Appearance
State
Allows an object to change its behavior when its internal state changes. The object appears to change its class.
The State pattern is often used with stateful objects.
What is a state? Taking QQ chat as an example, a user's QQ can have several states:
- Offline state (not logged in)
- Logging in state
- Online state
- Busy state (temporarily away)
How to represent these states? We can define an enum
to represent different states. However, different states require corresponding behaviors. For example, when receiving a message:
java
if (state == ONLINE) {
// Flash the icon
} else if (state == BUSY) {
reply("I'm busy now; I will reply later.");
} else if ...
The purpose of the State pattern is to break down the above lengthy if...else...
logic into different state classes, making it easier to add new states in the future.
For example, we can design a chatbot with two states:
- Disconnected
- Connected
For the disconnected state, we do not respond to messages:
java
public class DisconnectedState implements State {
public String init() {
return "Bye!";
}
public String reply(String input) {
return "";
}
}
For the connected state, we respond to received messages:
java
public class ConnectedState implements State {
public String init() {
return "Hello, I'm Bob.";
}
public String reply(String input) {
if (input.endsWith("?")) {
return "Yes. " + input.substring(0, input.length() - 1) + "!";
}
if (input.endsWith(".")) {
return input.substring(0, input.length() - 1) + "!";
}
return input.substring(0, input.length() - 1) + "?";
}
}
The key design idea of the State pattern is state switching, which we implement with a BotContext
to handle state transitions:
java
public class BotContext {
private State state = new DisconnectedState();
public String chat(String input) {
if ("hello".equalsIgnoreCase(input)) {
// Switch to connected state upon receiving "hello":
state = new ConnectedState();
return state.init();
} else if ("bye".equalsIgnoreCase(input)) {
// Switch to disconnected state upon receiving "bye":
state = new DisconnectedState();
return state.init();
}
return state.reply(input);
}
}
Thus, a valuable AI chatbot worth millions is born:
java
Scanner scanner = new Scanner(System.in);
BotContext bot = new BotContext();
for (;;) {
System.out.print("> ");
String input = scanner.nextLine();
String output = bot.chat(input);
System.out.println(output.isEmpty() ? "(no reply)" : "< " + output);
}
Let's see how it works:
> hello
< Hello, I'm Bob.
> Nice to meet you.
< Nice to meet you!
> Today is cold?
< Yes. Today is cold!
> bye
< Bye!
Practice
Add a BusyState
to represent the busy status.
Summary
The design idea of the State pattern is to separate the logic of different states into different state classes, making it easier to add new states.
The key to implementing the State pattern lies in state transitions. Simple state transitions can be directly specified by the caller, while complex state transitions can be triggered internally based on conditions.