State Pattern in C#
Tags: beginners, programming, tutorial, csharp
Published: 2022-09-22 15:56:19

The **State** is a behavioural design pattern that lets an object alter its behaviour when its internal state changes. For the system, it appears as if the object changed its class.

The **State** design pattern is one of the most useful patterns described by the Gang of Four. Games often depend on the State pattern because objects can change so frequently. Many other simulations, whether they are games or not, depend on the State pattern as well.

You can find the example code of this post, on [GitHub](https://github.com/Kalkwst/DesignPatterns)

## Conceptualizing the Problem

The State pattern is closely related to the *Finite-State Machine*.

![Example of a Finite-State Machine](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vqwl8kc9qva5dx827gr8.PNG)

The idea is that, at any given time, there's a *finite* number of *states* in which a program can be in. Within every unique state, the application behaves differently, and the program can be switched from one to the other instantaneously. However, depending on the current state, the application can only switch to certain states. The switching rules, called *transitions*, are predetermined.

This approach is very common around the web. Imagine we have a `Post` class on a blogging website. A post can be in one of three states: `Draft`, `Private Publish` and `Published`. The `Publish` method of the document works a little differently in each state:

- In the `Draft` state, it publishes the document with a secret URL.
- In the `Private Publish` state, it makes the document public.
- In the `Published` state, it doesn't do anything.

![Possible states and transitions of a post object. ](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ofq5tkqna64sdewur0rf.PNG)

State machines are usually implemented with long chains of conditional statements that select the appropriate behaviour depending on the object's current state. Usually, this state is just a set of variables. Even if you haven't heard about finite states before, you've probably implemented a state at least once.

```csharp
public class Post
{
	private string state;

	//...

	public void Publish()
	{
		switch(state)
		{
			"draft":
				state = "private publish"
				break;
			"private publish":
				state = "published"
				break;
			"public publish":
				// do nothing
				break;
		}
	}
}
```

The greatest weakness of a state machine based on conditionals reveals itself as soon as we start adding states and state-dependent behaviours. Most methods will contain humongous conditionals that pick the proper behaviour of a method according to the current state. This code is very difficult to maintain because any change to the transition logic might require changing conditionals in every method.

The problem only worsens with time. It's quite difficult to predict all possible states and transitions during the design phase. Hence, a lean state machine can grow into a bloated mess over time.

The **State** pattern suggests that we create new classes for all possible states of an object and extract all state-specific behaviours into these classes.

Instead of implementing all behaviours, 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.

![The document delegates the work to a state object](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/i75mr6du4m6myg6bkaw1.PNG)

To transition the context into another state, replace the active state object with another object that represents that new state. This is possible only if all state classes follow the same interface and the context itself works with these objects through that interface. 

This structure may look similar to the **Strategy** pattern, but there's one key difference. In the **State** pattern, the particular states may be aware of each other and initiate transitions from one state to another, whereas strategies rarely know about each other.

## Structuring the State Pattern

In its base implementation, the State pattern has four participants:

![Class Diagram of the State Pattern](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ysxso92rfoh9xtpn1cs9.PNG)

- **Context**: The **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. The context exposes a setter method for passing it a new state object. 
- **State**: The **State** interface declares the state-specific methods. These methods should make sense for all concrete states because we don't want some of our states to have methods that are never called.
- **Concrete State**: The **Concrete States** provide their implementations for the state-specific methods. To avoid duplication of similar code across multiple states, we may provide intermediate abstract classes encapsulating some common behaviour. State objects may store a backreference to the context object. Through this reference, the state can fetch any required info from the context object, and initiate state transitions. Both context and concrete states can set the next stage and perform the actual state transition by replacing the state object linked to the context.
- **Client**: The **Client** can trigger state changes to the **Context** object.

To demonstrate how the State pattern works, we will implement a component which keeps track of the internal temperature of a steak and assesses a level of "doneness" to it.

First, let's define our **State** participant, which represents the "doneness" level of a steak.

```csharp
using State.Context;

namespace State.State
{
    /// <summary>
    /// The State abstract class
    /// </summary>
    public abstract class Doneness
    {
        protected Steak steak;
        protected double currentTemperature;
        protected double lowerTemperature;
        protected double upperTemperature;
        protected bool isSafe;

        public Steak Steak
        {
            get { return steak; }
            set { steak = value; }
        }

        public double CurrentTemperature
        {
            get { return currentTemperature; }
            set { currentTemperature = value; }
        }

        public abstract void IncreaseTemperature(double degrees);
        public abstract void DecreaseTemperature(double degrees);
        public abstract void DonenessCheck();
    }
}
```

We have declared the `IncreaseTemperature()`, `DecreaseTemperature()`, and the `DonenessCheck()` abstract methods. These methods will be implemented by each of the states we can place the steak.

Now that we have the **State** participant, we will define some **ConcreteState** objects. First, we will define a state for when the steak is uncooked, and therefore not safe to eat. In this state, we can increase or decrease the cooking temperature, but the steak will not be safe for consumption until the core temperature is greater than 48.9 degrees Celsius. 

We will also implement the method `DonenessCheck()`, which determines whether or not the internal temperature of the steak is sufficiently high to allow it to move to another state. At this point, we'll assume that a steak can only move one state at a time.

```csharp
namespace State.State
{
    /// <summary>
    /// A Concrete State class.
    /// </summary>
    public class Uncooked : Doneness
    {
        public Uncooked(Doneness state)
        {
            currentTemperature = state.CurrentTemperature;
            steak = state.Steak;

            lowerTemperature = 0;
            upperTemperature = 48.9;
            isSafe = false;
        }

        public override void DecreaseTemperature(double degrees)
        {
            currentTemperature -= degrees;
            DonenessCheck();
        }

        public override void DonenessCheck()
        {
            if (currentTemperature > upperTemperature)
                steak.State = new Rare(this);
        }

        public override void IncreaseTemperature(double degrees)
        {
            currentTemperature += degrees;
            DonenessCheck();
        }
    }
}
```

Similarly, we can implement the rest of the states of the steak.

```csharp
using State.Context;

namespace State.State
{
    /// <summary>
    /// A Concrete State class.
    /// </summary>
    public class Rare : Doneness
    {
        public Rare(Doneness state) : this(state.CurrentTemperature, state.Steak) { }

        public Rare(double currentTemperature, Steak steak)
        {
            this.currentTemperature = currentTemperature;
            this.steak = steak;
            isSafe = true;

            lowerTemperature = 49;
            upperTemperature = 54.4;
        }

        public override void DecreaseTemperature(double degrees)
        {
            currentTemperature -= degrees;
            DonenessCheck();
        }

        public override void DonenessCheck()
        {
            if (currentTemperature < lowerTemperature)
                steak.State = new Uncooked(this);

            if (currentTemperature > upperTemperature)
                steak.State = new MediumRare(this);
        }

        public override void IncreaseTemperature(double degrees)
        {
            currentTemperature += degrees;
            DonenessCheck();
        }
    }
}
```

```csharp
using State.Context;

namespace State.State
{
    public class MediumRare : Doneness
    {
        public MediumRare(Doneness state) : this(state.CurrentTemperature, state.Steak) { }

        public MediumRare(double currentTemperature, Steak steak)
        {
            this.currentTemperature = currentTemperature;
            this.steak = steak;
            isSafe = true;

            lowerTemperature = 54.5;
            upperTemperature = 57.2;
        }

        public override void DecreaseTemperature(double degrees)
        {
            currentTemperature -= degrees;
            DonenessCheck();
        }

        public override void DonenessCheck()
        {
            if (currentTemperature < lowerTemperature)
                steak.State = new Rare(this);

            if (currentTemperature > upperTemperature)
                steak.State = new Medium(this);
        }

        public override void IncreaseTemperature(double degrees)
        {
            currentTemperature += degrees;
            DonenessCheck();
        }
    }
}
```

```csharp
using State.Context;

namespace State.State
{
    public class Medium : Doneness
    {
        public Medium(Doneness state) : this(state.CurrentTemperature, state.Steak) { }

        public Medium(double currentTemperature, Steak steak)
        {
            this.currentTemperature = currentTemperature;
            this.steak = steak;
            isSafe = true;

            lowerTemperature = 57.3;
            upperTemperature = 62.8;
        }

        public override void DecreaseTemperature(double degrees)
        {
            currentTemperature -= degrees;
            DonenessCheck();
        }

        public override void DonenessCheck()
        {
            if (currentTemperature < lowerTemperature)
                steak.State = new MediumRare(this);

            if (currentTemperature > upperTemperature)
                steak.State = new Well(this);
        }

        public override void IncreaseTemperature(double degrees)
        {
            currentTemperature += degrees;
            DonenessCheck();
        }
    }
}
```

```csharp
using State.Context;

namespace State.State
{
    public class Well : Doneness
    {
        public Well(Doneness state) : this(state.CurrentTemperature, state.Steak) { }

        public Well(double currentTemperature, Steak steak)
        {
            this.currentTemperature = currentTemperature;
            this.steak = steak;
            isSafe = true;

            lowerTemperature = 62.9;
            upperTemperature = 68.3;
        }

        public override void DecreaseTemperature(double degrees)
        {
            currentTemperature -= degrees;
            DonenessCheck();
        }

        public override void DonenessCheck()
        {
            if (currentTemperature < lowerTemperature)
                steak.State = new Medium(this);

            if (currentTemperature > upperTemperature)
                steak.State = new WellDone(this);
        }

        public override void IncreaseTemperature(double degrees)
        {
            currentTemperature += degrees;
            DonenessCheck();
        }
    }
}
```

```csharp
using State.Context;

namespace State.State
{
    public class WellDone : Doneness
    {
        public WellDone(Doneness state) : this(state.CurrentTemperature, state.Steak) { }

        public WellDone(double currentTemperature, Steak steak)
        {
            this.currentTemperature = currentTemperature;
            this.steak = steak;
            isSafe = true;

            lowerTemperature = 68.4;
            upperTemperature = 73.9;
        }

        public override void DecreaseTemperature(double degrees)
        {
            currentTemperature -= degrees;
            DonenessCheck();
        }

        public override void DonenessCheck()
        {
            if (currentTemperature < lowerTemperature)
                steak.State = new Well(this);

            if (currentTemperature > upperTemperature)
                steak.State = new Burnt(this);
        }

        public override void IncreaseTemperature(double degrees)
        {
            currentTemperature += degrees;
            DonenessCheck();
        }
    }
}
```

```csharp
using State.Context;

namespace State.State
{
    public class Burnt : Doneness
    {
        public Burnt(Doneness state) : this(state.CurrentTemperature, state.Steak) { }

        public Burnt(double currentTemperature, Steak steak)
        {
            this.currentTemperature = currentTemperature;
            this.steak = steak;
            isSafe = true;

            lowerTemperature = 74.0;
            upperTemperature = double.MaxValue;
        }

        public override void DecreaseTemperature(double degrees)
        {
            currentTemperature -= degrees;
            DonenessCheck();
        }

        public override void DonenessCheck()
        {
            if (currentTemperature < lowerTemperature)
                steak.State = new WellDone(this);

        }

        public override void IncreaseTemperature(double degrees)
        {
            currentTemperature += degrees;
            DonenessCheck();
        }
    }
}
```

Now that we have all of our states defined, we can finally implement our **Context** participant. In this case, the Context is a `Steak` class which maintains a reference to the `Doneness` state it is currently in. Further, whenever we increase or decrease the temperature of the steak, it must call the current `Doneness` state's corresponding method.

```csharp
using State.State;

namespace State.Context
{
    /// <summary>
    /// The Context class
    /// </summary>
    public class Steak
    {
        private Doneness state;
        private string cut;

        public Steak(string cut)
        {
            this.cut = cut;
            state = new Rare(0.0, this);
        }

        public double CurrentTemperature
        {
            get { return state.CurrentTemperature; }
        }

        public Doneness State
        {
            get { return state; }
            set { state = value; }
        }

        public void IncreaseTemperature(double degrees)
        {
            state.IncreaseTemperature(degrees);
            Console.WriteLine($"Increased degrees by {degrees} degrees");
            Console.WriteLine($"    Current degrees is {CurrentTemperature}");
            Console.WriteLine($"    Doneness is {State.GetType().Name}");
            Console.WriteLine("");
        }

        public void DecreaseTemperature(double degrees)
        {
            state.DecreaseTemperature(degrees);
            Console.WriteLine($"Decreased degrees by {degrees} degrees");
            Console.WriteLine($"    Current degrees is {CurrentTemperature}");
            Console.WriteLine($"    Doneness is {State.GetType().Name}");
            Console.WriteLine("");
        }
    }
}
```

Finally, in our `Main()` method, we can use these states by creating a `Steak` object and then changing its internal temperature:

```csharp
using State.Context;

Steak steak = new Steak("T-Bone");

steak.IncreaseTemperature(48.9);
steak.IncreaseTemperature(10);
steak.IncreaseTemperature(5);
steak.DecreaseTemperature(15);
steak.DecreaseTemperature(5);
steak.IncreaseTemperature(10);
steak.IncreaseTemperature(20);
```

As we change the temperature, we change the state of the `Steak` object. The output of this method looks like the following:

![Output of the State Pattern example](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/47k45f53hjowcsj5unyh.PNG)

As the `Steak` instance's internal temperature changes, the `Doneness` state in which it currently resides also changes, and consequently the apparent behaviour of that object shifts to whatever behaviour is defined by the current state.

## Pros and Cons of State Pattern
|||
|:--|:--|
|✔ We can organize the code related to particular states into separate classes, thus satisfying the *Single Responsibility Principle*|❌Applying the pattern can be overkill if a state machine has only a few states or rarely changes.|
|✔ We can introduce new states without changing existing state classes or the context, thus satisfying the *Open/Closed Principle*||
|✔ We can simplify the code of the context by eliminating bulky state machine conditionals.||

## Relations with Other Patterns
- The **Bridge**, the **State** and the **Strategy** patterns have very similar structures. Indeed, all of these patterns are based on composition, which is delegating work to other objects. However, they all solve different problems. A pattern isn’t just a recipe for structuring our code in a specific way. It can also communicate to other developers the problem the pattern solves.
- The **State** can be considered as an extension of the **Strategy** pattern. Both patterns are based on composition: they change the behaviour of the context by delegating some work to helper objects. _Strategy_ makes these objects completely independent and unaware of each other. However, _State_ doesn’t restrict dependencies between concrete states, letting them alter the state of the context at will.

## Final Thoughts

In this article, we have discussed what is the State pattern, when to use it and what are the pros and cons of using this design pattern. We then examined how the State pattern relates to other classic design patterns. 

It's worth noting that the State pattern, along with the rest of the design patterns presented by the Gang of Four, is not a panacea or a be-all-end-all solution when designing an application. Once again it's up to the engineers to consider when to use a specific pattern. After all these patterns are useful when used as a precision tool, not a sledgehammer.