08 Jan 2025

State Pattern

Overview

The State Pattern is a behavioral design pattern that allows an object to change its behavior when its internal state changes. It enables an object to appear as though its class has changed by delegating behavior to state objects.=

This pattern encapsulates state-specific behavior into separate classes, making it easy to add or modify states without altering the context or other states.

Key Participants

  1. Context

  • Maintains a reference to the current state.

  • Allows state transitions by delegating behavior to the current state object.

  1. State

  • Defines an interface for encapsulating the behavior associated with a particular state of the context.

  1. ConcreteState

  • Implements the state-specific behavior for a particular state.

Implementation in Code

Example: Vending Machine

// State Interface
interface State {
    void insertCoin();
    void dispenseItem();
}

// ConcreteState: No Coin
class NoCoinState implements State {
    private VendingMachine vendingMachine;

    public NoCoinState(VendingMachine vendingMachine) {
        this.vendingMachine = vendingMachine;
    }

    @Override
    public void insertCoin() {
        System.out.println("Coin inserted.");
        vendingMachine.setState(vendingMachine.getHasCoinState());
    }

    @Override
    public void dispenseItem() {
        System.out.println("Please insert a coin first.");
    }
}

// ConcreteState: Has Coin
class HasCoinState implements State {
    private VendingMachine vendingMachine;

    public HasCoinState(VendingMachine vendingMachine) {
        this.vendingMachine = vendingMachine;
    }

    @Override
    public void insertCoin() {
        System.out.println("Coin already inserted.");
    }

    @Override
    public void dispenseItem() {
        System.out.println("Item dispensed.");
        vendingMachine.setState(vendingMachine.getNoCoinState());
    }
}

// Context
class VendingMachine {
    private State noCoinState;
    private State hasCoinState;

    private State currentState;

    public VendingMachine() {
        noCoinState = new NoCoinState(this);
        hasCoinState = new HasCoinState(this);

        currentState = noCoinState; // Initial state
    }

    public void insertCoin() {
        currentState.insertCoin();
    }

    public void dispenseItem() {
        currentState.dispenseItem();
    }

    public void setState(State state) {
        this.currentState = state;
    }

    public State getNoCoinState() {
        return noCoinState;
    }

    public State getHasCoinState() {
        return hasCoinState;
    }
}

// Client
public class StatePatternDemo {
    public static void main(String[] args) {
        VendingMachine vendingMachine = new VendingMachine();

        vendingMachine.dispenseItem(); // No coin inserted
        vendingMachine.insertCoin();   // Insert coin
        vendingMachine.dispenseItem(); // Dispense item
        vendingMachine.dispenseItem(); // No coin inserted again
    }
}

Key Methods

  1. setState(State): Allows the context to transition between states.

  1. handle(Context): Defines behavior based on the current state.

Advantages

  1. Simplifies Complex Logic: Eliminates large conditionals or switch-case statements.

  1. Encapsulation: State-specific logic is encapsulated in separate classes.

  1. Ease of Extension: Adding new states or modifying existing ones does not require changes to other parts of the code.

Disadvantages

  1. Increased Number of Classes: Each state requires a separate class, which can lead to class explosion.

  1. Tight Coupling: States may need to reference the context to perform transitions, creating a dependency.

Real-World Examples

  1. Traffic Lights: Each light (red, yellow, green) represents a state, and transitions occur based on timers or triggers.

  1. Document Workflow: A document might be in states like Draft, Review, Published, or Archived.

  1. Media Player: States like Playing, Paused, and Stopped determine the behavior of the player.

When to Use the State Pattern?

  • When an object needs to change its behavior based on its state.

  • When state-specific behavior needs to be isolated into separate classes.

  • When adding new states should not require changing existing logic.

← Back to Library