Menu
Coddy logo textTech

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 published

The 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 icon

Challenge

Easy

Let'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 an ITrafficLightState interface in the Traffic namespace with a method Change(TrafficLight light). Then create three concrete state classes:
    • RedState - when Change is called, it prints Red - Stop! Changing to Green... and transitions the light to GreenState
    • GreenState - prints Green - Go! Changing to Yellow... and transitions to YellowState
    • YellowState - prints Yellow - Caution! Changing to Red... and transitions to RedState
  • TrafficLight.cs: Create a TrafficLight class in the same namespace. This is your context class that maintains the current state. It should start in RedState by default and have:
    • A SetState(ITrafficLightState state) method that state objects use to trigger transitions
    • A Change() method that delegates to the current state's Change method
  • Program.cs: Bring everything together by creating a TrafficLight and 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 published

The 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
    }
}
quiz iconTest yourself

This lesson includes a short quiz. Start the lesson to answer it and track your progress.

All lessons in Object Oriented Programming