The State Pattern consists of the following key components:
The context is the object whose behavior changes based on its state. It maintains a reference to the current state object and delegates the state-specific behavior to it.
This is an interface or an abstract class that defines the methods representing the state-specific behavior. All concrete state classes must implement this interface.
These classes implement the State Interface and provide the actual implementation of the state-specific behavior. Each concrete state class represents a particular state of the context.
Let’s take an example of a vending machine. A vending machine can be in different states such as “Idle”, “Has Money”, and “Dispensing Product”. Here is a simple UML-like representation of the components:
+-----------------+ +----------------+
| VendingMachine (Context) |<>------| State |
+-----------------+ +----------------+
| - currentState: State | | + insertMoney() |
| + insertMoney() | | + selectProduct()|
| + selectProduct() | | + dispenseProduct()|
+-----------------+ +----------------+
/\
/ \
/ \
+----------------+ +----------------+
| IdleState | | HasMoneyState |
+----------------+ +----------------+
| + insertMoney() | | + insertMoney() |
| + selectProduct()| | + selectProduct()|
| + dispenseProduct()| | + dispenseProduct()|
+----------------+ +----------------+
Let’s implement the vending machine example in Java.
// State interface
interface State {
void insertMoney();
void selectProduct();
void dispenseProduct();
}
// IdleState class
class IdleState implements State {
private VendingMachine vendingMachine;
public IdleState(VendingMachine vendingMachine) {
this.vendingMachine = vendingMachine;
}
@Override
public void insertMoney() {
System.out.println("Money inserted.");
vendingMachine.setState(vendingMachine.getHasMoneyState());
}
@Override
public void selectProduct() {
System.out.println("Please insert money first.");
}
@Override
public void dispenseProduct() {
System.out.println("Please insert money and select a product first.");
}
}
// HasMoneyState class
class HasMoneyState implements State {
private VendingMachine vendingMachine;
public HasMoneyState(VendingMachine vendingMachine) {
this.vendingMachine = vendingMachine;
}
@Override
public void insertMoney() {
System.out.println("You have already inserted money.");
}
@Override
public void selectProduct() {
System.out.println("Product selected. Dispensing...");
vendingMachine.setState(vendingMachine.getDispensingState());
}
@Override
public void dispenseProduct() {
System.out.println("Please select a product first.");
}
}
// DispensingState class
class DispensingState implements State {
private VendingMachine vendingMachine;
public DispensingState(VendingMachine vendingMachine) {
this.vendingMachine = vendingMachine;
}
@Override
public void insertMoney() {
System.out.println("Please wait for the product to be dispensed.");
}
@Override
public void selectProduct() {
System.out.println("Please wait for the product to be dispensed.");
}
@Override
public void dispenseProduct() {
System.out.println("Product dispensed.");
vendingMachine.setState(vendingMachine.getIdleState());
}
}
// VendingMachine class
class VendingMachine {
private State idleState;
private State hasMoneyState;
private State dispensingState;
private State currentState;
public VendingMachine() {
idleState = new IdleState(this);
hasMoneyState = new HasMoneyState(this);
dispensingState = new DispensingState(this);
currentState = idleState;
}
public void insertMoney() {
currentState.insertMoney();
}
public void selectProduct() {
currentState.selectProduct();
}
public void dispenseProduct() {
currentState.dispenseProduct();
}
public void setState(State state) {
this.currentState = state;
}
public State getIdleState() {
return idleState;
}
public State getHasMoneyState() {
return hasMoneyState;
}
public State getDispensingState() {
return dispensingState;
}
}
public class Main {
public static void main(String[] args) {
VendingMachine vendingMachine = new VendingMachine();
vendingMachine.insertMoney();
vendingMachine.selectProduct();
vendingMachine.dispenseProduct();
}
}
IdleState
.IdleState
, when money is inserted, the state changes to HasMoneyState
.The State Pattern is a powerful design pattern for managing object state changes. It allows for a clear separation of concerns, making the code more modular and maintainable. By encapsulating state-specific behavior in separate classes, it becomes easier to add new states or modify existing ones. When used correctly, the State Pattern can significantly improve the scalability and flexibility of your Java applications.