CodeDifficulty: Intermediate3 min

State Template in Python, Java and TypeScript

Two states that hand control to each other, and a context that never checks which it holds.

The State template is the pattern in its pure form, with the participant names from the class diagram. A context starts in state A and receives three requests; each state handles a request and then replaces itself with the other. The steps build the Python version, and the tabs give the complete program in three languages, each with its output.

Building the template one part at a time

State

One method per request, which receives the context.

py
@abstractmethod
def handle(self, context: "Context") -> None: ...

Concrete state

Does its work, then sets the next state.

py
def handle(self, context: "Context") -> None:
    print("A handles the request, then hands over to B")
    context.state = ConcreteStateB()

Context

Passes the request on, and never asks which state it holds.

py
def request(self) -> None:
    self.state.handle(self)

The complete template

py
from abc import ABC, abstractmethod


class State(ABC):
    @abstractmethod
    def handle(self, context: "Context") -> None: ...


class ConcreteStateA(State):
    def handle(self, context: "Context") -> None:
        print("A handles the request, then hands over to B")
        context.state = ConcreteStateB()


class ConcreteStateB(State):
    def handle(self, context: "Context") -> None:
        print("B handles the request, then hands over to A")
        context.state = ConcreteStateA()


class Context:
    def __init__(self, state: State) -> None:
        self.state = state

    def request(self) -> None:
        self.state.handle(self)


context = Context(ConcreteStateA())
for _ in range(3):
    context.request()
print("now in:", type(context.state).__name__)
OutputRan with python:3.12-slim (Python 3.12.13)
A handles the request, then hands over to B
B handles the request, then hands over to A
A handles the request, then hands over to B
now in: ConcreteStateB
`request` is one line, whichever state the context holds.
java
public class SaTemplate {
    public static void main(String[] args) {
        Context context = new Context(new ConcreteStateA());
        for (int i = 0; i < 3; i++) {
            context.request();
        }
        System.out.println("now in: " + context.getState().getClass().getSimpleName());
    }
}

interface State {
    void handle(Context context);
}

class ConcreteStateA implements State {
    public void handle(Context context) {
        System.out.println("A handles the request, then hands over to B");
        context.setState(new ConcreteStateB());
    }
}

class ConcreteStateB implements State {
    public void handle(Context context) {
        System.out.println("B handles the request, then hands over to A");
        context.setState(new ConcreteStateA());
    }
}

class Context {
    private State state;

    Context(State state) {
        this.state = state;
    }

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

    State getState() {
        return state;
    }

    void request() {
        state.handle(this);
    }
}
OutputRan with eclipse-temurin:21-jdk (Java 21.0.12)
A handles the request, then hands over to B
B handles the request, then hands over to A
A handles the request, then hands over to B
now in: ConcreteStateB
The states call `setState` on the context to change it.
ts
interface State {
  handle(context: Context): void;
}

class ConcreteStateA implements State {
  handle(context: Context): void {
    console.log("A handles the request, then hands over to B");
    context.state = new ConcreteStateB();
  }
}

class ConcreteStateB implements State {
  handle(context: Context): void {
    console.log("B handles the request, then hands over to A");
    context.state = new ConcreteStateA();
  }
}

class Context {
  constructor(public state: State) {}

  request(): void {
    this.state.handle(this);
  }
}

const context = new Context(new ConcreteStateA());
for (let i = 0; i < 3; i++) {
  context.request();
}
console.log("now in:", context.state.constructor.name);
OutputRan with node:22-slim (Node 22.23.1), TypeScript 5.9.3
A handles the request, then hands over to B
B handles the request, then hands over to A
A handles the request, then hands over to B
now in: ConcreteStateB
The context's `state` is public, so a state can replace it.

The three requests alternate between A and B, and the context ends in B. Context.request contains no conditional: the behaviour changed each time because the object behind self.state changed.

Each state receives the context as an argument, which is how it can replace the context's state. The cost is a two-way dependency: the context knows the state interface, and each state knows the context. Java keeps the field private and exposes setState; Python and TypeScript assign the field directly. Both are common, and the choice is how much of the context the states may touch.