[← Design patterns index](README.md) # State **Category:** Behavioral | **Source:** `patterns/state/BadCarState/` + `patterns/state/CarState/` ## Problem Behavior depends on an internal state field, so every state-dependent method `switch`es on it. As states and behaviors multiply, each method grows a branch per state, and changing a transition rule means finding and editing that conditional in _every_ method that has one. ## Solution Make one class per state, all sharing a common `State` base class. The **context** holds a reference to its _current_ state object and delegates state-dependent calls to it instead of branching. Each state class implements only what makes sense in that state, and states drive transitions by handing the context a new state object. The object "appears to change its class" because you're swapping what it delegates to. ## Participants (GoF names → this repo) - **Context** → `Car` — holds the current `State`, delegates, exposes `ChangeState`. - **State** → `State` base class — declares the state-specific operations. - **ConcreteState** → `Off`, `Stopped`, `Driving`. ```mermaid stateDiagram-v2 [*] --> Off Off --> Stopped : turn-clockwise / engine has started Stopped --> Off : turn-counter / engine has stopped Stopped --> Driving : gas / car begins to drive Driving --> Stopped : brake / car slows to a stop Driving --> Off : turn-counter / engine off, car stops suddenly Stopped --> Stopped : turn-clockwise / grinding noise Driving --> Driving : turn-clockwise / grinding noise ``` ```mermaid classDiagram class Car { -State _state +HandleInput(string) bool +ChangeState(State) } class State { #Car _car +TurnKeyClockwise() virtual +TurnKeyCounterClockwise() virtual +PressGas() virtual +PressBrake() virtual } Car o--> State : delegates State <|-- Off State <|-- Stopped State <|-- Driving Off ..> Car : calls ChangeState Stopped ..> Car : calls ChangeState Driving ..> Car : calls ChangeState ``` ## Before — `BadCarState`: a `switch` inside a `switch` ```csharp public class Car { private enum State { Off, Stopped, Driving } private State _state = State.Off; public bool HandleInput(string input) { switch (input) { case "turn-clockwise": switch (_state) // <-- nested state switch #1 { case State.Off: Console.WriteLine("The engine has started"); _state = State.Stopped; break; case State.Stopped: case State.Driving: Console.WriteLine("You hear a grinding noise..."); break; } break; case "turn-counter": switch (_state) // <-- nested state switch #2 { case State.Stopped: Console.WriteLine("The engine has stopped"); _state = State.Off; break; case State.Driving: Console.WriteLine("The engine turns off, and the car stops suddenly"); _state = State.Off; break; } break; case "gas": switch (_state) // <-- nested state switch #3 { case State.Stopped: Console.WriteLine("The car begins to drive"); _state = State.Driving; break; } break; case "brake": switch (_state) // <-- nested state switch #4 { case State.Driving: Console.WriteLine("The car slows to a stop"); _state = State.Stopped; break; } break; case "exit": return false; } return true; } } ``` Adding a `Reversing` state means auditing **all four** nested switches. And notice there is no `case State.Off` under `"gas"` — pressing the gas while off silently does nothing, and nothing in the code says whether that was intentional or an oversight. ## After — `CarState`: one class per state ```csharp // States/State.cs — base State. Virtual empty bodies = "this input is a no-op here." public class State { protected Car _car; // backreference, so states can transition public State(Car car) { _car = car; } public virtual void TurnKeyClockwise() { } public virtual void TurnKeyCounterClockwise() { } public virtual void PressGas() { } public virtual void PressBrake() { } } // States/Off.cs — overrides exactly one operation; the rest are inherited no-ops public class Off : State { public Off(Car car) : base(car) { } public override void TurnKeyClockwise() { Console.WriteLine("The engine has started"); _car.ChangeState(new Stopped(_car)); // the state drives its own transition } } // States/Stopped.cs public class Stopped : State { public Stopped(Car car) : base(car) { } public override void PressGas() { Console.WriteLine("The car begins to drive"); _car.ChangeState(new Driving(_car)); } public override void TurnKeyClockwise() { Console.WriteLine("You hear a grinding noise..."); // valid input, no transition } public override void TurnKeyCounterClockwise() { Console.WriteLine("The engine has stopped"); _car.ChangeState(new Off(_car)); } } // States/Driving.cs public class Driving : State { public Driving(Car car) : base(car) { } public override void TurnKeyClockwise() { Console.WriteLine("You hear a grinding noise..."); } public override void TurnKeyCounterClockwise() { Console.WriteLine("The engine turns off, and the car stops suddenly"); _car.ChangeState(new Off(_car)); } public override void PressBrake() { Console.WriteLine("The car slows to a stop"); _car.ChangeState(new Stopped(_car)); } } ``` ```csharp // Car.cs — the Context. All state branching is gone. public class Car { private State _state; public Car() { ChangeState(new Off(this)); // initial state } public bool HandleInput(string input) { switch (input) // switches on INPUT only, never on state { case "turn-clockwise": _state.TurnKeyClockwise(); break; case "turn-counter": _state.TurnKeyCounterClockwise(); break; case "gas": _state.PressGas(); break; case "brake": _state.PressBrake(); break; case "exit": return false; } return true; } public void ChangeState(State state) { _state = state; } } ``` What actually improved: - `Car` went from four nested state-switches to four one-line delegations. It no longer knows the state names at all beyond the initial `Off`. - Adding `Reversing` = adding **one file**. No existing method changes. - Each state's complete behavior is readable in one place, instead of scattered across four switch arms. - "Gas while off does nothing" is now an explicit, deliberate design statement: `Off` doesn't override `PressGas`, so the base class's empty body runs. - **Trade-off:** the transition graph is now distributed across the state classes rather than visible in one switch. That's why the state diagram shown earlier is worth keeping next to the code. ## Output (verified — both projects, same scripted input) Input: `gas`, `turn-clockwise`, `gas`, `turn-clockwise`, `brake`, `turn-counter`, `exit` ``` Welcome to the Driving Simulater Usage: turn-clockwise -Turns the car's key clockwise turn-counter -Turns the car's key counter clockwise gas -Presses the gas pedal brake -Presses the brake pedal exit -Exits the simulator > <- "gas" while Off: silently ignored > The engine has started > The car begins to drive > You hear a grinding noise... > The car slows to a stop > The engine has stopped > goodbye ``` `BadCarState` and `CarState` produce **byte-identical output**. That's the headline: the refactor changed structure and nothing else. The first blank prompt is the silent no-op discussed earlier, present in both versions. ## GoF pseudocode (audio player) ``` class AudioPlayer is // Context field state: State constructor AudioPlayer() is this.state = new ReadyState(this) method changeState(state: State) is this.state = state method clickPlay() is state.clickPlay() // pure delegation method startPlayback() is // service method a state can call back into abstract class State is protected field player: AudioPlayer constructor State(player) is this.player = player abstract method clickLock() abstract method clickPlay() class ReadyState extends State is method clickPlay() is player.startPlayback() player.changeState(new PlayingState(player)) class PlayingState extends State is method clickPlay() is player.stopPlayback() player.changeState(new ReadyState(player)) class LockedState extends State is method clickPlay() is // Locked, so do nothing. ``` GoF makes `State` **abstract** with abstract methods, forcing every state to answer every input explicitly. The repo instead uses virtual empty bodies, so "do nothing" is the default. Abstract = the compiler catches states you forgot to handle; virtual = less boilerplate. Pick deliberately. ## Applicability - An object's behavior depends on its state and must change at run-time accordingly, the number of states is large, and state-specific code changes often. - Operations have large multipart conditionals branching on the object's state (usually enum values), and _several_ operations repeat that same conditional structure. - Lots of duplicate code across similar states — factor it into intermediate abstract state classes. ## Pros / Cons | Pros | Cons | | ------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- | | Single Responsibility — each state's code in its own class | Overkill for a machine with few states that rarely change | | Open/Closed — add states without touching existing ones | More classes; the transition graph is no longer visible in one place | | Kills the bulky conditionals in the context | Allocates a new state object on every transition (usually irrelevant; states can be cached/shared if stateless) | | Makes transitions explicit instead of stray field assignments | | ## Seen in the wild (.NET) - `TcpConnection`-style protocol handling — GoF's own example. - Workflow/order-status engines. - Game character controllers. - `HttpClient`'s connection lifecycle. ## Related patterns - **Bridge / State / Strategy** share the composition-delegation structure and differ in intent. - **State vs. Strategy** — State is arguably Strategy where the delegates _know about each other_ and actively swap themselves in (`_car.ChangeState(new Stopped(_car))`). Strategy objects are independent and mutually unaware, and are chosen by the client rather than by each other. --- **← Prev:** [Observer](observer.md) · [All patterns](README.md)