Table of Contents
Introduction
The State design pattern is a behavioral software design pattern. The state pattern is meant to make the internal state of an object decide its behavior. In other words, you give an object a specific state and then it will act differently based on that state.
With the State pattern comes to following terminology:
- The Context is the class that has an internal state and changes behavior according to it.
- The State usually defines an interface (or abstract class) that a concrete State class needs to implement.
- The ConcreteState class implements the actual behavior for the Context.
Use Cases
- You need an object to behave differently based on the internal state.
- A vending machine.
- A TCP Connection listener class.
Pitfalls
- Too many states can become complex.
- If you have multiple State machines, be sure to keep State implementations clearly apart if they’re not interchangeable.
Java Implementation
I think one of the most clear to understand examples of this pattern is a vending machine. For this example I made only two states for the machine, idle and dispensing. Other states, such as maintenance mode or system jammed could be added. You might even choose to have separate states for idle with coins and idle without coins, but I chose to keep it as simple as possible.
Main
The Main class only creates a new VendingMachine object, which is called the Context of the State design pattern.
package com.chosengambit.designpatterns;
import com.chosengambit.state.VendingMachine;
public class Main {
public static void main(String[] args) {
VendingMachine vm = new VendingMachine();
new Thread(vm).start();
}
}JavaVendingMachine
In this example, the VendingMachine is a Runnable class because it keeps listening to console input. The Idle state is inserted as default. It keeps tracks of the number of coins in the machine.
package com.chosengambit.state;
import com.chosengambit.state.states.AbstractVendingMachineState;
import com.chosengambit.state.states.IdleState;
import java.util.Scanner;
/**
* This is the Context of the State pattern
* @author ChosenGambit
*/
public class VendingMachine implements Runnable {
private int coins = 0;
private Scanner scanner;
private volatile AbstractVendingMachineState state;
/**
* Set a State for the Vending Machine
* @param state
*/
public void setState(AbstractVendingMachineState state) {
this.state = state;
if (state instanceof Runnable) {
new Thread(state).start();
}
}
public void addCoin() {
this.coins++;
}
public void substractCoin() {
this.coins--;
}
public void resetCoins() {
this.coins = 0;
}
public int getCoins() {
return this.coins;
}
@Override
public void run() {
System.out.println("Vending Machinea active.");
this.setState(new IdleState(this));
while (true) {
try {
if (scanner != null) {
String input = this.scanner.nextLine();
input = input.toLowerCase().trim();
state.action(input);
}
else {
this.scanner = new Scanner(System.in);
}
}
catch(Exception e) {
System.out.println(e.getMessage());
break;
}
}
}
}
JavaAbstractVendingMachineState
The AbstractVendingMachineState class is the base class for all states that are compatible with the VendingMachine.
package com.chosengambit.state.states;
import com.chosengambit.state.StateInterface;
import com.chosengambit.state.VendingMachine;
/**
* The abstract state for a VendingMachine, to make sure all states
* have the correct context
* @author ChosenGambit
*/
public abstract class AbstractVendingMachineState implements StateInterface {
protected VendingMachine vendingMachine;
public AbstractVendingMachineState(VendingMachine vendingMachine) {
this.vendingMachine = vendingMachine;
}
@Override
public abstract void action(String action);
@Override
public abstract void run();
}
JavaStateInterface
This interface is implemented by the AbstractVendingMachineState. It is not really necessary to have it split up like this right now, but this way you could later implement a method that does accept states that are not Runnable.
package com.chosengambit.state;
/**
* The StateInterface, all states must implement an action
* @author ChosenGambit
*/
public interface StateInterface extends Runnable {
public void action(String action);
}
JavaIdleState
The Idle state listens to different letters as input, to simulate interacting with the Vending Machine.
package com.chosengambit.state.states;
import com.chosengambit.state.VendingMachine;
/**
* The Machine does nothing and is standing by.
* @author ChosenGambit
*/
public class IdleState extends AbstractVendingMachineState {
public IdleState(VendingMachine vendingMachine) {
super(vendingMachine);
}
public void action(String action) {
int coins = vendingMachine.getCoins();
switch (action) {
// Insert Coin
case "i" -> {
if (coins < 10) {
vendingMachine.addCoin();
System.out.println("Coin inserted, there are now "+(coins+1)+ " coin(s) in the machine.");
System.out.print("Please provide your input: ");
}
else {
System.out.println("You cannot insert another coin.");
}
}
// Reset Coins
case "r" -> {
if (coins > 0) {
vendingMachine.resetCoins();
System.out.println("All "+coins+" coins have been returned.");
System.out.print("Please provide your input: ");
}
else {
System.out.println("There are no coins in the Machine.");
}
}
// Cola
case "c" -> {
if (coins > 0) {
vendingMachine.substractCoin();
vendingMachine.setState(new DispenseState(vendingMachine, "Cola"));
}
else {
System.out.println("There are no coins in the Machine.");
}
}
// Orange Soda
case "o" -> {
if (coins > 0) {
vendingMachine.substractCoin();
vendingMachine.setState(new DispenseState(vendingMachine, "Orange Soda"));
}
else {
System.out.println("There are no coins in the Machine.");
}
}
default -> {
System.out.println("Unknown input, please insert 'i', 'r', 'c' or 'o'");
}
}
}
@Override
public void run() {
System.out.println("i = Insert coin");
System.out.println("r = Reset coins");
System.out.println("c = Pick cola");
System.out.println("o = Pick orange soda");
System.out.print("Please provide your input: ");
}
}
JavaDispenseState
The DispenseState’s action method only propagates that it is busy. This state simulates the Vending Machine dispense a product.
package com.chosengambit.state.states;
import com.chosengambit.state.VendingMachine;
/**
* The Machine is busy trying to dispense a product
* @author ChosenGambit
*/
public class DispenseState extends AbstractVendingMachineState {
private volatile boolean dispensing = false;
private String product;
public DispenseState(VendingMachine vendingMachine, String product) {
super(vendingMachine);
this.product = product;
}
public synchronized boolean isDispensing() {
return this.dispensing;
}
@Override
public void action(String action) {
if (this.dispensing) {
System.out.println("Machine is busy with dispensing a "+product);
}
}
@Override
public void run() {
try {
System.out.println("The Machine will now try to dispend a product, please stand-by.");
if (!this.dispensing && this.vendingMachine != null) {
this.dispensing = true;
System.out.println("Busy dispensing a "+product+".");
Thread.sleep(4000);
this.dispensing = false;
System.out.println("A(n) "+product+" has been dispensed.");
vendingMachine.setState(new IdleState(vendingMachine));
}
}
catch(Exception e) {
System.out.println(e.getMessage());
}
}
}
JavaClass Diagram

The VendingMachine holds a single state. It also listens to any user input which it passes to the state, which then decides what to do with the VendingMachine based on the input. The IdleState can listen to several commands, while the DispenseState makes the VendingMachine unreachable for a while.
Conclusion
The State design pattern is great to change the behavior of the context, decided by the State itself. You can implement as many states as you want, but it is recommended to be somewhat reserved with the amount to use. If you use more state patterns in a single project, be sure to use clear and distinctive names for what states can be used with what context.
References
Freeman, E., Bates, B., & Sierra, K. (2004). Head first design patterns.
Gamma, E., Helm, R., Johnson, R., & Vlissides, J. (1994). Design Patterns: Elements of Reusable Object-Oriented Software.
Wikipedia contributors. (2024). Software design pattern. Wikipedia. https://en.wikipedia.org/wiki/Software_design_pattern