Monday, March 15, 2010

Design Patterns – State Pattern

The State design pattern is a behavioural pattern. The behaviour of object changes accordingly to its internal state. Instead of implementing if-then-else like structure inside the object to switch the behaviour, we move the logic to property "State" which has method Handle() and call this method. We change the behaviour of the object as we just switch the value of the "State" property.

The class which behaviour will change accordingly to its state is named Context. The Context class contains property State which is of type that implements common IState interface.

We have different implementations of IState - StateOne, StateTwo etc. We assign new IState object to the property "State" of the Context object and the behaviour of the Context object changes.


Traffic Light



Imagine we are going to implement traffic light. Accordingly to Wikipedia we have three possible lights: red; amber and green. The traffic light cycle may vary, but we are going to implement 3-way traffic light cycle.


  • Red light - Means prohibits the traffic from proceeding.

  • Green light - Then follows the green light that allows to proceed.

  • Yellow light - Next is yellow light denoting if safe to, prepare to stop.



We will use the State pattern to implement a traffic light. The traffic light (TrafficLight) object is the context object. The traffic light has public properties for the color of the light currently turned on, it knows for how long this light should stay, and knows light of which color will follow after that. But setting these properties is done by State object.

We have 3 different states for the three traffic light colors. All they have method Handle() which is invoked by the TrafficLight object.


IState interface



All the concrete State classes have to implement interface ITrafficLightState. The concrete traffic light state handles request from the Context object and thereof the behaviour of the Context object changes.


interface ITrafficLightState
{
void Handle(TrafficLight trafficLight);
}


State implementations



For the different traffic light colors we implement three different State objects. All they implement interface ITrafficLightState:


// State "Red Light"
class RedLightState : ITrafficLightState
{
public void Handle(TrafficLight trafficLight)
{
trafficLight.Color = "Red"; // Switch the traffic light color
trafficLight.Pause = 5; // For 5 seconds
trafficLight.NextState = new GreenLightState(); // Then green light follows
}
}

// State "Amber Light"
class AmberLightState : ITrafficLightState
{
public void Handle(TrafficLight trafficLight)
{
trafficLight.Color = "Amber"; // Switch the traffic light color
trafficLight.Pause = 2; // For 2 seconds
trafficLight.NextState = new RedLightState(); // Then red light follows
}
}

// State "Green Light"
class GreenLightState : ITrafficLightState
{
public void Handle(TrafficLight trafficLight)
{
trafficLight.Color = "Green"; // Switch the traffic light color
trafficLight.Pause = 5; // For 5 seconds
trafficLight.NextState = new AmberLightState(); // Then Amber light follows
}
}



TrafficLight context object



How we are ready to implement the TrafficLight context object:


// Context - "Traffic Light", has internal state - one of the above.
// The behaviour changes accordingly to the current state
class TrafficLight
{
// Has properties that define it's behaviour
public int Pause { get; set; }
public string Color { get; set; }

// Has internal State
public ITrafficLightState NextState { get; set; }

// Sets the default state
public TrafficLight()
{
this.NextState = new RedLightState();
}


// Receives request ...
public void Request()
{
// ... then:
// Delegates to the internal State to handle.
NextState.Handle(this);

// The behaviour has changed, color, time of light, etc.
Console.WriteLine(Color);
System.Threading.Thread.Sleep(Pause * 1000);
}
}



And with this we are ready to give a try of our implementation, which looks like this now:





To demonstrate the traffic light we run an infinite loop and call the method Request() on instance of TrafficLight:


static void Main(string[] args)
{
// Demonstrate how TrafficLight works
TrafficLight trafficLight = new TrafficLight();
while (true)
trafficLight.Request();
}



Usage of State pattern



In our example the state of the Context object was changed from the state object itself. But it is not necessarily to be done this way. We can implement a Coffee Machine to run Short, Tall, Grand coffee. Then the state would be changed pressing a button on the Machine. Or we could use State pattern to switch between simple and advanced preview on a form. Then probably the Handle() function will just change the preview but not the user preferences.

Atanas Hristov

kick it on DotNetKicks.com
Shout it

7 comments:

  1. Blogs are so informative where we get lots of information on any topic. Nice job keep it up!!
    _____________________________

    Dissertation Editing

    ReplyDelete
  2. Thanks for the another great article. I have gone through your post. Nicely written on selected topic. keep sharing your best post in the future as well. it will give more info to us and thank for this wonderful post.

    whm reseller hosting | reseller web hosting

    ReplyDelete
  3. I see in this article very well explained subject that many people are interested with.

    cheap webhosting
    cheap web hosting

    ReplyDelete
  4. google was created by tow people you where about to complete a project to there college and they came by this massive search engine google it means 1 fallowed by 100 zeros.
    Shopping Cart Solutions

    ReplyDelete
  5. I all ways wonder about the designing web site vary useful thanks for sharing.
    Shopping Cart Solutions

    ReplyDelete
  6. Thanks for sharing this info there useful blog.
    Shopping Cart Solutions

    ReplyDelete
  7. Nice post. I recently posted about an implementation using C# and Loan states as an example. may be readers of this blog might find it useful

    http://www.nileshgule.com/2012/07/state-design-pattern.html

    ReplyDelete