State Pattern
Part of the Object Oriented Programming section of Coddy's C# journey — lesson 60 of 70.
The State pattern is a behavioral pattern that allows an object to change its behavior when its internal state changes. Instead of using complex conditional statements to handle different states, you encapsulate each state in its own class and delegate behavior to the current state object.
The pattern consists of three components: a state interface defining state-specific behavior, concrete state classes implementing that behavior, and a context class that maintains a reference to the current state:
public interface IDocumentState
{
void Publish(Document doc);
}
public class DraftState : IDocumentState
{
public void Publish(Document doc)
{
Console.WriteLine("Moving to moderation");
doc.SetState(new ModerationState());
}
}
public class ModerationState : IDocumentState
{
public void Publish(Document doc)
{
Console.WriteLine("Publishing document");
doc.SetState(new PublishedState());
}
}
public class PublishedState : IDocumentState
{
public void Publish(Document doc)
{
Console.WriteLine("Already published");
}
}The context holds the current state and delegates actions to it. State objects can trigger transitions by calling SetState() on the context:
public class Document
{
private IDocumentState _state = new DraftState();
public void SetState(IDocumentState state) => _state = state;
public void Publish() => _state.Publish(this);
}
var doc = new Document();
doc.Publish(); // Moving to moderation
doc.Publish(); // Publishing document
doc.Publish(); // Already publishedThe State pattern eliminates sprawling if-else or switch statements that check the current state. Each state class handles only its own behavior, making the code easier to maintain and extend with new states.
Challenge
EasyLet's build a traffic light system using the State pattern. Traffic lights cycle through distinct states (red, yellow, green), and each state determines what happens when the light changes. Instead of using complex conditionals, you'll encapsulate each light state in its own class.
You'll organize your code across three files:
TrafficLightState.cs: Define anITrafficLightStateinterface in theTrafficnamespace with a methodChange(TrafficLight light). Then create three concrete state classes:RedState- whenChangeis called, it printsRed - Stop! Changing to Green...and transitions the light toGreenStateGreenState- printsGreen - Go! Changing to Yellow...and transitions toYellowStateYellowState- printsYellow - Caution! Changing to Red...and transitions toRedState
TrafficLight.cs: Create aTrafficLightclass in the same namespace. This is your context class that maintains the current state. It should start inRedStateby default and have:- A
SetState(ITrafficLightState state)method that state objects use to trigger transitions - A
Change()method that delegates to the current state'sChangemethod
- A
Program.cs: Bring everything together by creating aTrafficLightand cycling through state changes based on input.
You will receive one input:
- The number of times to change the traffic light (an integer)
Create a TrafficLight (which starts at red) and call Change() the specified number of times.
For example, if the input is 3, the output should be:
Red - Stop! Changing to Green...
Green - Go! Changing to Yellow...
Yellow - Caution! Changing to Red...If the input is 5, the light cycles back around:
Red - Stop! Changing to Green...
Green - Go! Changing to Yellow...
Yellow - Caution! Changing to Red...
Red - Stop! Changing to Green...
Green - Go! Changing to Yellow...Notice how the TrafficLight class contains no logic about what each color means or what comes next - all that knowledge lives in the individual state classes. Each state is responsible for its own behavior and for triggering the transition to the next state!
Cheat sheet
The State pattern is a behavioral pattern that allows an object to change its behavior when its internal state changes. Instead of using complex conditional statements, you encapsulate each state in its own class and delegate behavior to the current state object.
The pattern consists of three components:
- State interface: Defines state-specific behavior
- Concrete state classes: Implement the behavior for each state
- Context class: Maintains a reference to the current state
Example implementation:
// State interface
public interface IDocumentState
{
void Publish(Document doc);
}
// Concrete state classes
public class DraftState : IDocumentState
{
public void Publish(Document doc)
{
Console.WriteLine("Moving to moderation");
doc.SetState(new ModerationState());
}
}
public class ModerationState : IDocumentState
{
public void Publish(Document doc)
{
Console.WriteLine("Publishing document");
doc.SetState(new PublishedState());
}
}
public class PublishedState : IDocumentState
{
public void Publish(Document doc)
{
Console.WriteLine("Already published");
}
}
// Context class
public class Document
{
private IDocumentState _state = new DraftState();
public void SetState(IDocumentState state) => _state = state;
public void Publish() => _state.Publish(this);
}
// Usage
var doc = new Document();
doc.Publish(); // Moving to moderation
doc.Publish(); // Publishing document
doc.Publish(); // Already publishedThe context holds the current state and delegates actions to it. State objects can trigger transitions by calling SetState() on the context. This eliminates sprawling if-else or switch statements, making the code easier to maintain and extend with new states.
Try it yourself
using System;
using Traffic;
class Program
{
public static void Main(String[] args)
{
// Read the number of times to change the traffic light
int n = Convert.ToInt32(Console.ReadLine());
// TODO: Create a TrafficLight instance (starts at RedState by default)
// TODO: Call Change() method n times using a loop
}
}
This lesson includes a short quiz. Start the lesson to answer it and track your progress.
All lessons in Object Oriented Programming
1Fundamentals of OOP
External FilesNamespaces & DirectivesIntro to Classes & ObjectsThe 'this' KeywordMethods and ParametersFields vs PropertiesConstructorsObject InitializersRecap - Simple Calculator4Inheritance
Basic Inheritance (:) SyntaxThe 'base' KeywordVirtual & Override KeywordsSealed ClassesThe 'object' Base ClassRecap - Employee Hierarchy7Advanced Features
Operator OverloadingIndexers (this[])ToString() OverrideExtension MethodsRecap - Custom List2Properties & Static Members
Auto-Implemented PropertiesRead/Write-Only PropertiesStatic Fields & MethodsStatic ClassesExpression-Bodied Members5Polymorphism & Interfaces
Compile vs Runtime PolyInterface vs Abstract ClassMultiple InterfacesExplicit InterfacesUpcasting & DowncastingRecap - Shape Calculator8Advanced OOP Concepts
Composition over InheritanceGenerics (Classes & Methods)Delegates and EventsAttributes and ReflectionIDisposable & using StatementDependency Injection Basics11Design Patterns Part 2
Command PatternAdapter PatternDecorator PatternTemplate Method PatternState PatternComposite Pattern