You can see this and other great articles on design patterns here.

The State Design Pattern allows you to alter the behavior, or the state, of the object at runtime. Each behavior is represented by a state class, and the behavior can change from one state to another.

Let’s look at the UML of the State Design Pattern first, then we will look at an example to see how it works. Below is the UML of the state pattern:

  • The IState interface defines the behaviors that the states can handle.
    • It has the Handle method that specifies the behavior of the state.
  • The ConcreteState class are the classes that represents different behaviors. You will have multiple ConcreteState classes where each class will have its own implementation of behavior.
    • It has the Handle method that implements the state behavior, and it is within this method that you can change from one state to another.
  • The Context class is the environment in which the states operates in.
    • It has the Request method that calls the IState's methods.

Let’s see an example. Assuming you are writing a game where the player is a warrior that goes into different battles. Based on the outcome of each battle the warrior can become strong, super strong, normal, or weak. These will be the different states that the warrior can be in. Also the warrior is more likely to transition to certain states based on the state he was in before he goes into the battle. If the warrior is in the strong state before he goes into the battle, he is more likely to become super strong at the end of the battle than if he started out weak.

Below is the UML of our example:

  • The IHealth interface defines methods for the states.
    • It has the DoBattle method when it's in a certain state of health.
  • The SupreStrong, Strong, Normal, and Weak classes are all the health states, or behavior, that the warrior can be in.
    • It has the DoBattle method implementation, and it is here where the warrior can move from one health state to another.
  • The Warrior class is the player, or the environment the health states operates in. The warrior can go into battle with a certain health state.
    • It has the Battle method, where it calls the DoBattle method of its health variable.
    • It has the SetHealth method that can set the health of the warrior.

One can also take the state pattern another step further by creating the IWarrior interface and decouple the relationship between the Warrior class and the IHealth interface. This way you can have different types of warrior where each warrior type will transition to different health differently. But for the purpose of demonstrating the state pattern only we will not do this in the implementation code below.

A comparison between the UML of the state pattern and the strategy pattern shows striking similarities. Both pattern defines the behaviors as the concrete classes, and both pattern defines the context in which the behavior runs under. But there are clear differences. The state pattern uses the behavior to switch to other behaviors, while the strategy pattern lets the client code choose the behavior it needs.

Below are the implementation code and the output of the state pattern. Notice that the warrior can switch from one state to another without the client code having to determine the health state of the warrior:

class Program
{
    static void Main(string[] args)
    {
        Warrior w = new Warrior();
        w.ShowHealth();
        w.Battle();
        w.ShowHealth();
        w.Battle();
        w.ShowHealth();
        w.Battle();
        w.ShowHealth();
        w.Battle();
        w.ShowHealth();
    }
}

public interface IHealth
{
    void DoBattle(Warrior w);
}

public class Warrior
{
    private IHealth health = new Normal();  //start as normal health

    public void Battle()
    {
        health.DoBattle(this);  //calls the health to exhibit the behavior
    }

    public void SetHealth(IHealth health)
    {
        this.health = health;
    }

    public void ShowHealth()
    {
        Console.WriteLine("Warrior is now: " + health.GetType().ToString());
    }
}

public class SuperStrong : IHealth
{
    void IHealth.DoBattle(Warrior w)
    {
        //warrior can transition to another state based on the outcome
        w.SetHealth(new Normal());
    }
}

public class Strong : IHealth
{
    void IHealth.DoBattle(Warrior w)
    {
        //warrior can transition to another state based on the outcome
        w.SetHealth(new SuperStrong());
    }
}

public class Normal : IHealth
{
    void IHealth.DoBattle(Warrior w)
    {
        //warrior can transition to another state based on the outcome
        w.SetHealth(new Weak());
    }
}

public class Weak : IHealth
{
    void IHealth.DoBattle(Warrior w)
    {
        //warrior can transition to another state based on the outcome
        w.SetHealth(new Strong());
    }
}

Liked this article? You can see this and other great articles on design patterns here.

推荐.NET配套的通用数据层ORM框架:CYQ.Data 通用数据层框架