State Design Pattern (Behavioral)
State is a behavioral design pattern that lets an object alter its behavior when its internal state changes. It appears as if the object changed its class.
Problem
Section titled βProblemβThe State pattern is closely related to the concept of a Finite State Machine. At any given moment, thereβs a finite number of states which a program can be in. Within any unique state, the program behaves differently, and the program can be switched from one state to another instantaneously.
However, these transitions are dependent on the current state. If you try to implement this using massive conditionals (if or switch statements), the code becomes extremely difficult to maintain. Any change to the transition logic requires changing conditionals in every method of the class.
Solution
Section titled βSolutionβThe State pattern suggests that you create new classes for all possible states of an object and extract all state-specific behaviors into these classes.
Instead of implementing all behaviors on its own, the original object, called context, stores a reference to one of the state objects that represents its current state, and delegates all the state-related work to that object. To transition the context into another state, you replace the active state object with another object that represents that new state.
Real-World Analogy
Section titled βReal-World AnalogyβThe buttons and switches on your smartphone behave differently depending on the current state of the device:
- When the phone is unlocked, pressing buttons leads to various actions.
- When the phone is locked, pressing any button leads to the unlock screen.
- When the phoneβs charge is low, pressing buttons might show a warning.
Structure
Section titled βStructureβ- Context: Stores a reference to one of the concrete state objects and delegates to it all state-specific work. The context communicates with the state object via the state interface.
- State Interface: Declares the state-specific methods. These methods should make sense for all concrete states.
- Concrete States: Provide their own implementations for the state-specific methods. To avoid duplication of similar code in multiple states, you may provide intermediate abstract classes that encapsulate common behavior.
- Transitioning: Both Context and Concrete States can set the next state of the context and perform the actual state transition by replacing the state object linked to the context.
Structure (Class Diagram)
Section titled βStructure (Class Diagram)βclassDiagram
class Context {
-state: State
+Context(initialState)
+changeState(state)
+request()
}
class State {
<<interface>>
+handle(context)
}
class ConcreteStateA {
+handle(context)
}
class ConcreteStateB {
+handle(context)
}
Context o-- State
State <|-- ConcreteStateA
State <|-- ConcreteStateB
Pseudocode
Section titled βPseudocodeβ# The base state class declares methods that all concrete# states should implement.abstract class State: protected field player: AudioPlayer constructor State(player): this.player = player
abstract method clickLock() abstract method clickPlay() abstract method clickNext()
# Concrete states implement behaviors associated with a state.class PlayingState extends State: method clickLock(): player.changeState(new LockedState(player)) method clickPlay(): player.stopPlayback() player.changeState(new ReadyState(player)) method clickNext(): player.nextSong()
# The Context class has a reference to the current state.class AudioPlayer: field state: State field UI, volume, playlist, currentSong
constructor AudioPlayer(): this.state = new ReadyState(this)
method changeState(state): this.state = state
method clickLock(): state.clickLock()Applicability
Section titled βApplicabilityβ- Dynamic Behavior: Use the State pattern when you have an object that behaves differently depending on its current state, the number of states is enormous, and the state-specific code changes frequently.
- Massive Conditionals: Use the pattern when you have a class polluted with massive conditionals that alter how the class behaves according to the current values of the classβs fields.
- Code Duplication: Use State when you have a lot of duplicate code across similar states and transitions of a condition-based state machine.
How to Implement
Section titled βHow to Implementβ- Declare the state interface.
- For every actual state, create a class that implements the state interface.
- In the context class, add a reference field of the state interface type and a public setter that allows overriding the value of that field.
- Replace the state conditionals in the context with calls to corresponding methods of the state object.
- To change the state of the context, create an instance of one of the state classes and pass it to the context. You can do this from the context itself, or from concrete states.
Pros and Cons
Section titled βPros and ConsβPros:
- Single Responsibility Principle: Organize the code related to particular states into separate classes.
- Open/Closed Principle: Introduce new states without changing existing state classes or the context.
- Simplify the code of the context by eliminating bulky state machine conditionals.
Cons:
- Applying the pattern can be overkill if a state machine has only a few states or rarely changes.
Relations with other Design Patterns
Section titled βRelations with other Design Patternsβ- Bridge, Strategy, and State (and to some degree Adapter) have very similar structures based on composition. However, they all solve different problems.
- Strategy makes these objects completely independent and unaware of each other. State doesnβt restrict dependencies between concrete states, letting them alter the state of the context at will.
Code Examples
Section titled βCode ExamplesβCode Examples (C#)
Section titled βCode Examples (C#)β// Statepublic interface IState{ void Handle(Context context);}
// Concrete Statespublic class StateA : IState{ public void Handle(Context context) { Console.WriteLine("State A β switch to B"); context.SetState(new StateB()); }}
public class StateB : IState{ public void Handle(Context context) { Console.WriteLine("State B β switch to A"); context.SetState(new StateA()); }}
// Contextpublic class Context{ private IState _state;
public Context(IState state) { _state = state; }
public void SetState(IState state) { _state = state; }
public void Request() { _state.Handle(this); }}
// Usageclass Program{ static void Main() { var context = new Context(new StateA());
context.Request(); context.Request(); context.Request(); }}Code Examples (Python)
Section titled βCode Examples (Python)βfrom __future__ import annotationsfrom abc import ABC, abstractmethod
class Context: """ The Context defines the interface of interest to clients. It also maintains a reference to an instance of a State subclass, which represents the current state of the Context. """ _state = None
def __init__(self, state: State) -> None: self.transition_to(state)
def transition_to(self, state: State): print(f"Context: Transition to {type(state).__name__}") self._state = state self._state.context = self
def request1(self): self._state.handle1()
def request2(self): self._state.handle2()
class State(ABC): """ The base State class declares methods that all Concrete State should implement and also provides a backreference to the Context object. """ @property def context(self) -> Context: return self._context
@context.setter def context(self, context: Context) -> None: self._context = context
@abstractmethod def handle1(self) -> None: pass
@abstractmethod def handle2(self) -> None: pass
class ConcreteStateA(State): def handle1(self) -> None: print("ConcreteStateA handles request1.") print("ConcreteStateA wants to change the state of the context.") self.context.transition_to(ConcreteStateB())
def handle2(self) -> None: print("ConcreteStateA handles request2.")
class ConcreteStateB(State): def handle1(self) -> None: print("ConcreteStateB handles request1.")
def handle2(self) -> None: print("ConcreteStateB handles request2.") print("ConcreteStateB wants to change the state of the context.") self.context.transition_to(ConcreteStateA())
if __name__ == "__main__": context = Context(ConcreteStateA()) context.request1() context.request2()Code Examples (C++)
Section titled βCode Examples (C++)β#include <iostream>#include <memory>
// Forward declarationclass Context;
// Stateclass IState {public: virtual ~IState() = default; virtual void Handle(Context& context) = 0;};
// Concrete Statesclass StateA : public IState {public: void Handle(Context& context) override;};
class StateB : public IState {public: void Handle(Context& context) override;};
// Contextclass Context {private: std::unique_ptr<IState> state;
public: Context(std::unique_ptr<IState> initialState) : state(std::move(initialState)) {}
void SetState(std::unique_ptr<IState> newState) { state = std::move(newState); }
void Request() { state->Handle(*this); }};
// Implementations of Handle()void StateA::Handle(Context& context) { std::cout << "State A β switch to B" << std::endl; context.SetState(std::make_unique<StateB>());}
void StateB::Handle(Context& context) { std::cout << "State B β switch to A" << std::endl; context.SetState(std::make_unique<StateA>());}
// Usageint main() { Context context(std::make_unique<StateA>());
context.Request(); context.Request(); context.Request();
return 0;}Code Examples (Java)
Section titled βCode Examples (Java)β// Statepublic interface IState { void handle(Context context);}
// Concrete Statespublic class StateA implements IState {
@Override public void handle(Context context) { System.out.println("State A β switch to B"); context.setState(new StateB()); }}
public class StateB implements IState {
@Override public void handle(Context context) { System.out.println("State B β switch to A"); context.setState(new StateA()); }}
// Contextpublic class Context {
private IState state;
public Context(IState state) { this.state = state; }
public void setState(IState state) { this.state = state; }
public void request() { state.handle(this); }}
// Usagepublic class Program { public static void main(String[] args) { Context context = new Context(new StateA());
context.request(); context.request(); context.request(); }}Code Examples (JavaScript)
Section titled βCode Examples (JavaScript)β// State (simulated interface)class IState { handle(context) { throw new Error("handle() must be implemented"); }}
// Concrete Statesclass StateA extends IState { handle(context) { console.log("State A β switch to B"); context.setState(new StateB()); }}
class StateB extends IState { handle(context) { console.log("State B β switch to A"); context.setState(new StateA()); }}
// Contextclass Context { constructor(state) { this.state = state; }
setState(state) { this.state = state; }
request() { this.state.handle(this); }}
// Usageconst context = new Context(new StateA());
context.request();context.request();context.request();Code Examples (Visual Basic)
Section titled βCode Examples (Visual Basic)β' ============================' State' ============================Public Interface IState Sub Handle(context As Context)End Interface
' ============================' Concrete States' ============================Public Class StateA Implements IState
Public Sub Handle(context As Context) Implements IState.Handle Console.WriteLine("State A β switch to B") context.SetState(New StateB()) End SubEnd Class
Public Class StateB Implements IState
Public Sub Handle(context As Context) Implements IState.Handle Console.WriteLine("State B β switch to A") context.SetState(New StateA()) End SubEnd Class
' ============================' Context' ============================Public Class Context Private _state As IState
Public Sub New(state As IState) _state = state End Sub
Public Sub SetState(state As IState) _state = state End Sub
Public Sub Request() _state.Handle(Me) End SubEnd Class
' ============================' Usage' ============================Module Program Sub Main() Dim context As New Context(New StateA())
context.Request() context.Request() context.Request() End SubEnd ModuleCode Examples (Rust)
Section titled βCode Examples (Rust)β// Statetrait State { fn handle(&self, context: &mut Context);}
// Concrete Statesstruct StateA;
impl State for StateA { fn handle(&self, context: &mut Context) { println!("State A β switch to B"); context.set_state(Box::new(StateB)); }}
struct StateB;
impl State for StateB { fn handle(&self, context: &mut Context) { println!("State B β switch to A"); context.set_state(Box::new(StateA)); }}
// Contextstruct Context { state: Box<dyn State>,}
impl Context { fn new(state: Box<dyn State>) -> Self { Self { state } }
fn set_state(&mut self, state: Box<dyn State>) { self.state = state; }
fn request(&mut self) { self.state.handle(self); }}
// Usagefn main() { let mut context = Context::new(Box::new(StateA));
context.request(); context.request(); context.request();}Code Examples (PHP)
Section titled βCode Examples (PHP)β<?php
// Stateinterface IState{ public function handle(Context $context): void;}
// Concrete Statesclass StateA implements IState{ public function handle(Context $context): void { echo "State A β switch to B\n"; $context->setState(new StateB()); }}
class StateB implements IState{ public function handle(Context $context): void { echo "State B β switch to A\n"; $context->setState(new StateA()); }}
// Contextclass Context{ private IState $state;
public function __construct(IState $state) { $this->state = $state; }
public function setState(IState $state): void { $this->state = $state; }
public function request(): void { $this->state->handle($this); }}
// Usage$context = new Context(new StateA());
$context->request();$context->request();$context->request();Code Examples (Go)
Section titled βCode Examples (Go)βpackage main
import ( "fmt")
// Statetype State interface { Handle(ctx *Context)}
// Concrete State Atype StateA struct{}
func (StateA) Handle(ctx *Context) { fmt.Println("State A β switch to B") ctx.SetState(StateB{})}
// Concrete State Btype StateB struct{}
func (StateB) Handle(ctx *Context) { fmt.Println("State B β switch to A") ctx.SetState(StateA{})}
// Contexttype Context struct { state State}
func NewContext(initial State) *Context { return &Context{state: initial}}
func (c *Context) SetState(s State) { c.state = s}
func (c *Context) Request() { c.state.Handle(c)}
// Usagefunc main() { ctx := NewContext(StateA{})
ctx.Request() ctx.Request() ctx.Request()}Code Examples (Ruby)
Section titled βCode Examples (Ruby)β# Stateclass Statedef handle(context) raise NotImplementedError, "Subclasses must implement handle"endend
# Concrete Statesclass StateA < Statedef handle(context) puts "State A β switch to B" context.set_state(StateB.new)endend
class StateB < Statedef handle(context) puts "State B β switch to A" context.set_state(StateA.new)endend
# Contextclass Contextdef initialize(state) @state = stateend
def set_state(state) @state = stateend
def request @state.handle(self)endend
# Usagecontext = Context.new(StateA.new)
context.requestcontext.requestcontext.requestCode Examples (Perl)
Section titled βCode Examples (Perl)β#!/usr/bin/perluse strict;use warnings;
############################# State (Interface-like)############################package State;
sub handle { die "handle() must be implemented by subclass";}
############################# Concrete State A############################package StateA;our @ISA = ('State');
sub new { bless {}, shift }
sub handle { my ($self, $context) = @_; print "State A β switch to B\n"; $context->set_state(StateB->new());}
############################# Concrete State B############################package StateB;our @ISA = ('State');
sub new { bless {}, shift }
sub handle { my ($self, $context) = @_; print "State B β switch to A\n"; $context->set_state(StateA->new());}
############################# Context############################package Context;
sub new { my ($class, $state) = @_; return bless { state => $state }, $class;}
sub set_state { my ($self, $state) = @_; $self->{state} = $state;}
sub request { my ($self) = @_; $self->{state}->handle($self);}
############################# Usage############################package main;
my $context = Context->new(StateA->new());
$context->request();$context->request();$context->request();Code Examples (TypeScript)
Section titled βCode Examples (TypeScript)β// Stateinterface IState { handle(context: Context): void;}
// Concrete Statesclass StateA implements IState { handle(context: Context): void { console.log("State A β switch to B"); context.setState(new StateB()); }}
class StateB implements IState { handle(context: Context): void { console.log("State B β switch to A"); context.setState(new StateA()); }}
// Contextclass Context { private state: IState;
constructor(state: IState) { this.state = state; }
setState(state: IState): void { this.state = state; }
request(): void { this.state.handle(this); }}
// Usageconst context = new Context(new StateA());
context.request();context.request();context.request();