RO EN

State Pattern – changing behavior based on state

State Pattern – changing behavior based on state
Doru Bulubasa
16 January 2026

In real applications, objects rarely have fixed behavior. Most of the time, the way they react depends on the internal state they are in. For example:

  • an order can be New, Processed, Delivered, or Cancelled

  • a document can be Draft, Published, or Archived

  • a user can be Active, Suspended, or Blocked

A naive approach would involve using many if / switch statements in the code, which quickly leads to:

  • hard-to-read code

  • violation of the Open/Closed principle

  • great difficulties when adding new states

State Pattern elegantly solves this problem.


What is State Pattern?

State Pattern allows an object to change its behavior when its internal state changes. The object will appear to have changed its class.

The main idea is:

  • each state is represented by a separate class

  • the state-specific behavior is moved into that class

  • the main object (context) delegates behavior to the current state


Problem: state-dependent behavior

Let's take a simple example: an order (Order).

Wrong approach (anti-pattern)

public enum OrderStatus
{
    New,
    Paid,
    Shipped,
    Cancelled
}

public class Order
{
    public OrderStatus Status { get; set; }

    public void Process()
    {
        if (Status == OrderStatus.New)
        {
            Console.WriteLine("Processing new order");
        }
        else if (Status == OrderStatus.Paid)
        {
            Console.WriteLine("Preparing shipment");
        }
        else if (Status == OrderStatus.Shipped)
        {
            Console.WriteLine("Order has already been shipped");
        }
        else if (Status == OrderStatus.Cancelled)
        {
            throw new InvalidOperationException("Order is cancelled");
        }
    }
}

❌ Problems:

  • scattered logic

  • methods that grow uncontrollably

  • frequent changes to the Order class


Solution: State Pattern

Pattern structure

  • State – common interface for all states

  • ConcreteState – concrete implementations for each state

  • Context – the object that changes its behavior


Implementation in C#

1. The IOrderState interface

public interface IOrderState
{
    void Process(OrderContext context);
}

2. Concrete states

The NewOrderState state

public class NewOrderState : IOrderState
{
    public void Process(OrderContext context)
    {
        Console.WriteLine("Processing new order");
        context.SetState(new PaidOrderState());
    }
}

The PaidOrderState state

public class PaidOrderState : IOrderState
{
    public void Process(OrderContext context)
    {
        Console.WriteLine("Preparing shipment");
        context.SetState(new ShippedOrderState());
    }
}

The ShippedOrderState state

public class ShippedOrderState : IOrderState
{
    public void Process(OrderContext context)
    {
        Console.WriteLine("Order has already been shipped");
    }
}

The CancelledOrderState state

public class CancelledOrderState : IOrderState
{
    public void Process(OrderContext context)
    {
        throw new InvalidOperationException("Order is cancelled");
    }
}

3. The OrderContext context

public class OrderContext
{
    private IOrderState _state;

    public OrderContext(IOrderState initialState)
    {
        _state = initialState;
    }

    public void SetState(IOrderState state)
    {
        _state = state;
    }

    public void Process()
    {
        _state.Process(this);
    }
}

4. Usage

var order = new OrderContext(new NewOrderState());

order.Process(); // New -> Paid
order.Process(); // Paid -> Shipped
order.Process(); // Shipped

Advantages

✅ eliminates if / switch

✅ respects the Open/Closed Principle

✅ each state is isolated and easy to test

✅ cleaner and extensible code


Disadvantages

⚠️ larger number of classes

⚠️ can be overkill for simple scenarios


When to use State Pattern?

✔️ behavior changes significantly depending on the state

✔️ you have many transitions between states

✔️ you want to avoid complex conditional logic

❌ NOT recommended when you have only 1–2 simple states


Difference from Strategy Pattern

State Strategy
The state changes from inside The strategy changes from outside
State-dependent flow Interchangeable algorithms

Conclusion

State Pattern is ideal for modeling real processes (workflows, orders, documents, payments), where behavior evolves over time.

When applied correctly, it leads to:

  • more readable code

  • fewer bugs

  • true extensibility