State is a behavioural design pattern that allows object to alter its behaviour when its internal state changes. There is a finite number of states which a program can be in, within any unique state, the program behaves differently, and the program can switch from on state to another. Each concrete state class knows which state should come next. The state object can be pre-created for further use or can be created when needed.
Applicability
- You want object to behave differently depending on its current state.
- You have complex conditional statements to manage an object’s behaviour based on its state
- You anticipate your object will need to support additional states in the future.
Approach
- Identify the context by determine the object for which you want to manage different states.
- Define the state interface that represents the various states and behaviours the context can perform when in different states.
- Create concrete states that each represents a specific state.
Components
- Context
- It is the client’s interface to the state machine
- Maintain an instance of a concrete state that defines the current state.
- State (abstract)
- Defines an interface for all concrete states, each method in the interface corresponds to a possible behaviour that can be performed at a particular state.
- Concrete state
- Each concrete state implements the abstract state with encapsulated behaviour associated with a specific state of context.
Example 1
Pre-create state objects and never destroy them
- Useful for frequent state changes, save costs of recreating states
- Context must keep reference to all states
This example models a simple fan with three states, the fan states can be adjusted.
Identify state pattern components
Context: Fan
State: FanState
Concrete state: OffState
, LowState
,HighState
State
State interface defines the common actions can be performed on the fan, it defines the contracts for the concrete states.
interface FanState {
void turnOn();
void turnOff();
void increaseSpeed();
void decreaseSpeed();
}
Concrete state
Each concrete state implements the state interface, providing specific behaviour for actions defined in the interface.
class OffState implements FanState {
@Override
public void turnOn() {
System.out.println("Turning the fan on to low speed.");
fan.setState(fan.getLowState());
}
@Override
public void turnOff() {
System.out.println("Fan is already off.");
}
@Override
public void increaseSpeed() {
System.out.println("Can't increase speed. Fan is off.");
}
@Override
public void decreaseSpeed() {
System.out.println("Can't decrease speed. Fan is off.");
}
}
class LowState implements FanState {
@Override
public void turnOn() {
System.out.println("Fan is already on at low speed.");
}
@Override
public void turnOff() {
System.out.println("Turning the fan off.");
fan.setState(fan.getOffState());
}
@Override
public void increaseSpeed() {
System.out.println("Increasing fan speed to high.");
fan.setState(fan.getHighState());
}
@Override
public void decreaseSpeed() {
System.out.println("Can't decrease speed. Fan is already on low.");
}
}
class HighState implements FanState {
@Override
public void turnOn() {
System.out.println("Fan is already on at high speed.");
}
@Override
public void turnOff() {
System.out.println("Turning the fan off.");
fan.setState(fan.getOffState());
}
@Override
public void increaseSpeed() {
System.out.println("Can't increase speed. Fan is already on high.");
}
@Override
public void decreaseSpeed() {
System.out.println("Decreasing fan speed to low.");
fan.setState(fan.getLowState());
}
}
Context
The context Fan
maintains a reference to the current state. The context is responsible for transitioning between different states by setting the current state. The states are pre-created for state switching and will never be destroyed.
class Fan {
private FanState currentState;
private FanState offState;
private FanState lowState;
private FanState highState;
public Fan() {
// states are pre-created
offState = new OffState();
lowState = new LowState();
highState = new HighState();
// Initially, the fan is off.
currentState = OffState;
}
public void setState(FanState state) {
this.currentState = state;
}
// getters for states
public FanState getOffState() {
return offState;
}
// ...
public void turnOn() {
currentState.turnOn();
}
public void turnOff() {
currentState.turnOff();
}
// ...
}
Client
The client triggers state transitions and observing responses based on the fan’s state.
// Example Usage
public class Main {
public static void main(String[] args) {
Fan fan = new Fan();
fan.turnOn(); // Turning on (low speed)
fan.increaseSpeed(); // Increasing speed (high speed)
fan.decreaseSpeed(); // Decreasing speed (low speed)
fan.turnOff(); // Turning off
fan.increaseSpeed(); // Trying to increase speed (fan is off)
}
}
Example 2
Only when they are needed and destroy them thereafter States are not known at runtime and context change states frequently
State
The state remains unchanged from the previous example.
Concrete state
The concrete state remains unchanged from the previous example.
Context
The sate is created when needed and is unreferenced and cleaned by the JVM garbage collection mechanism afterwards.
class Fan {
private FanState currentState;
public Fan() {
// inital state is undefined
sate = null;
}
public void setState(FanState state) {
this.currentState = state;
}
public void turnOn() {
if (state == null) {
// create state object when needed
currentState = new LowState();
System.out.println("Turning fan on to low spped.");
} else {
System.out.println("Fan is already on");
}
}
public void turnOff() {
if (currentState != null) {
System.out.println("Turning fan off");
// unreference the state and dystroyed by JVM gabage collector
currentState = null;
} else {
System.out.println("Fan is already off.");
}
}
public void increaseSpeed() {
if (currentState != null) {
currentState.increaseSpeed(this);
} else {
System.out.println("Cannot increase speed, fan is off.");
}
}
public void decreaseSpeed() {
if (currentState != null) {
currentState.decreaseSpeed(this);
} else {
System.out.println("Cannot decrease speed, fan is off.");
}
}
}
Client
The concrete state remains unchanged from the previous example.
In this example, sate objects are created only when needed and destroyed afterwards. This approach allows you to create and destroy state objects dynamically based on fan’s state.
Back to parent page: Behavioural Patterns
Design_Pattern Behavioural_Design_Patterns SOFT2201 State_pattern