Design Pattern articles in this series are listed below:
Design Patterns - An Overview
Design Patterns – Using the Strategy Pattern in C#
Design Patterns – Using the Singleton Pattern in C#
Design Patterns – Using the Adapter Pattern in C#
Design Patterns - Using the Facade Pattern in C#
Design Patterns - Using the Composite Pattern in C#
Design Patterns - Using the Decorator Pattern in C#
Design Patterns - Using the Abstract Factory Pattern in C#
Design Patterns – Using the Builder Pattern in C#
Design Patterns - Using the Observer Pattern in C# (Events and Delegates)
Design Patterns – Using the Chain of Responsibility Pattern in C#
Design Patterns – Using the State Pattern in C#
Design Patterns – Which to use When? Take the Quiz!
What is the State Pattern?
The State Pattern is a behavioral pattern that can be used to alter the behavior of an object at run time. As the state of an object changes, the functionality of the object can change drastically. This change of behavior is hidden from the Client and the Client interfaces with a wrapper object known as the Context. The State Pattern is a dynamic version of the Strategy Pattern.
How is the State Pattern implemented?
We know from our Chemistry class at school that water can exist in 3 different forms – Solid, Liquid and Gas. The state of water changes when it’s heated or cooled. The behavior of water (ex: molecular structure, shape, mass) is different in different states. This can be represented by using the State Pattern.
Illustration - Different states of Water
The State Pattern has the following players:
1. Context – A wrapper that hides the implementation of change of state and the behavior.
2. State – An abstract class that is a base class for all State classes.
3. State1, State2.. StateN – Concrete implementations of the base State class. Each State class has state specific implementations.
4. Client – A class that uses an instance of the Context class and provides parameters that may cause a change of state.
Let’s look at the implementation for each of the players and map them to the ones in our example. First stop is the Context class implementation called Matter.
//Context class - Matter
public class Matter
{
public State State { get; set; }
public int Temperature { get; set; }
public Matter()
{
Temperature = 30; //Initialize to 30 degrees celsius
State = new Liquid(); //Initialize to liquid
}
public void HeatBy(int temperature)
{
State.HeatBy(temperature, this);
}
public void CoolBy(int temperature)
{
State.CoolBy(temperature, this);
}
}
The Matter class has an instance of the State class and a property called Temperature that tracks the current temperature of Matter. The HeatBy and the CoolBy methods add or remove temperature from the matter.
Let’s take a look at the Abstract base State class.
//Abstract Base State class - State
public abstract class State
{
public void HeatBy(int temperature, Matter matter)
{
matter.Temperature += temperature;
AdjustState(matter);
}
public void CoolBy(int temperature, Matter matter)
{
matter.Temperature -= temperature;
AdjustState(matter);
}
public void AdjustState(Matter matter)
{
if (matter.Temperature <= 0)
matter.State = new Solid();
if (matter.Temperature > 0 && matter.Temperature < 100)
matter.State = new Liquid();
if (matter.Temperature >= 100)
matter.State = new Gas();
}
public abstract string GetState();
}
When matter is heated or cooled, the state is adjusted in the AdjustState method. The concrete classes in this example are simple. in reality, these could have more state specific implementations.
//Concrete State 1 - Solid
public class Solid : State
{
public override string GetState()
{
return "Solid";
}
}
//Concrete State 2 - Liquid
public class Liquid : State
{
public override string GetState()
{
return "Liquid";
}
}
//Concrete State 3 - Gas
public class Gas : State
{
public override string GetState()
{
return "Gas";
}
}
Let’s see how all this comes together in the Client. The Client never interacts with the States directly. Instead it uses an instance of the Context class. In our example, we initialize the temperature to 30 degrees Celsius. We heat/cool by different temperatures and demonstrate the state change.
class Program
{
static void Main(string[] args)
{
Matter Water = new Matter();
//Step 1: Start with a temperature of 30 degrees and state of Liquid
Console.WriteLine(String.Format("Current Temperature is {0}. Current State is {1}", Water.Temperature.ToString(), Water.State.GetState()));
//Step 2: Heat by 100 degrees
Water.HeatBy(100);
Console.WriteLine(String.Format("Current Temperature is {0}. Current State is {1}", Water.Temperature.ToString(), Water.State.GetState()));
//Step 3: Cool by 200 degrees
Water.CoolBy(200);
Console.WriteLine(String.Format("Current Temperature is {0}. Current State is {1}", Water.Temperature.ToString(), Water.State.GetState()));
//Step 4: Cool again by 50 degrees
Water.CoolBy(50);
Console.WriteLine(String.Format("Current Temperature is {0}. Current State is {1}", Water.Temperature.ToString(), Water.State.GetState()));
}
}
Here’s the output of the program.
Physical Model – Class Diagram
Conclusion
Consider using the State Pattern when you are solving problems in which the behavior of an object changes based on it’s state. The State Pattern is flexible as it makes it easy to introduce a new state.
Take a look at other Design Pattern articles on this website. You can also subscribe to the RSS feed and/or follow us on Twitter.