Loading ...

Design Pattern: State design pattern | CodeAsp.Net

Design Pattern: State design pattern

(9669)
4.61
/5
Avg: 4.61/5: (6 votes)
Published: 4/6/2013 by Raghav Khunger

Introduction:
The State pattern allows an object to change its behaviour when its internal state changes. This pattern comes under Behavioral Patterns section. This pattern is used to represent the state of an object. By "internal state changes" it means, the object will appear to change its type at runtime.

UML Class Diagram:


Real world example of State Pattern:
We are going to take a real world example of Lift Door to understand the concept of State Pattern. Door can remain in the following states: Opened, Closed, Closing and Opening. The person who wants to use the lift can open or close the door by pressing the respective button for controlling the door. Now taking this into consideration and looking the above UML diagram we have

  1. Context- Door
  2. State- DoorState
  3. Concrete States- Opened, Closed, Closing and Opening

We are going to make six classes based on the above. Door, DoorState, Opened,Closed, Closing and Opening. DoorState will be an abstract class with the following methods Close and Open.

UML Diagram for real world lift door example: 



Different States of Lift Door:

Above, the timeout represents the time in which the door will automatically starts closing from its opened state. Let's start writing code for the above:

DoorState (Abstract class):

 public abstract class DoorState
    {
        protected Door Door { get; set; }
        protected int TimeoutInterval { get; set; }
        public abstract void Open();
        public abstract void Close();

        public AutoResetEvent TimeoutCallBackHandler { get; set; }
    }

The TimeoutCallBackHandler represents the call back handler which represents the code to execute when specified time has elapsed in particular state.

Concrete Classes - OpeningState, ClosingState, ClosedState and OpenedState:

    public class OpeningState : DoorState
    {
        public OpeningState(Door door)
        {
            this.Door = door;
            this.TimeoutInterval = 5;

            //Door will open after n number of seconds
            this.TimeoutCallBackHandler = Utilities.CallWithTimeout(() =>
                                                                 {
                                                                     if (this.TimeoutCallBackHandler == null) return;
                                                                     this.TimeoutCallBackHandler=null;
                                                                     this.Door.State = new OpenedState(this.Door);
                                                                     this.Door.Status = "Door is opened.";
                                                                     this.Door.OnStateChange();
                                                                 }, this.TimeoutInterval);
        }
        public override void Open()
        {
            this.Door.Status = "Door is already opening.";
            this.Door.OnStateAlert();
        }

        public override void Close()
        {
            this.TimeoutCallBackHandler=null;
            this.Door.State = new ClosingState(this.Door);
            this.Door.Status = "Door is closing.";
            this.Door.OnStateChange();
        }
    }

    public class ClosingState : DoorState
    {
        public ClosingState(Door door)
        {
            this.Door = door;
            this.TimeoutInterval = 5;

            //Door will close after n number of seconds
            this.TimeoutCallBackHandler=Utilities.CallWithTimeout(() =>
            {
                if (this.TimeoutCallBackHandler == null) return;
                this.TimeoutCallBackHandler=null;
                this.Door.State = new ClosedState(this.Door);
                this.Door.Status = "Door is closed.";
                this.Door.OnStateChange();
            }, this.TimeoutInterval);
        }

        public override void Open()
        {
            this.TimeoutCallBackHandler=null;
            this.Door.State = new OpeningState(this.Door);
            this.Door.Status = "Door is opening.";
            this.Door.OnStateChange();
        }

        public override void Close()
        {
            this.Door.Status = "Door is already closing.";
            this.Door.OnStateAlert();
        }

    }

    public class ClosedState : DoorState
    {
        public ClosedState(Door door)
        {
            this.Door = door;
        }
        public override void Open()
        {
            this.TimeoutCallBackHandler=null;
            this.Door.State = new OpeningState(this.Door);
            this.Door.Status = "Door is opening.";
            this.Door.OnStateChange();
        }

        public override void Close()
        {
            this.Door.Status = "Door is already closed.";
            this.Door.OnStateAlert();
        }
    }

    public class OpenedState : DoorState
    {
        public OpenedState(Door door)
        {
            this.Door = door;
            this.TimeoutInterval = 10;

            //After n number of seconds start closing the door by itself
            this.TimeoutCallBackHandler = Utilities.CallWithTimeout(()=>
            {
                if (this.TimeoutCallBackHandler == null) return;
                this.TimeoutCallBackHandler=null;
                this.Door.State = new ClosingState(this.Door);
                this.Door.Status = "Door is closing.";
                this.Door.OnStateChange();
            }, this.TimeoutInterval);
        }
        public override void Open()
        {
            this.Door.Status = "Door is already opened.";
            this.Door.OnStateAlert();
        }

        public override void Close()
        {
            this.TimeoutCallBackHandler=null;
            this.Door.State = new ClosingState(this.Door);
            this.Door.Status = "Door is closing.";
            this.Door.OnStateChange();
        }

    }


Door- The context:

 public class Door
    {
        public DoorState State { get; set; }
        public string Status { get; set; }

        public delegate void StateChangeEventHandler(Door door);
        public delegate void StateAlertEventHandler(Door door);
        public event StateChangeEventHandler StateChange;
        public event StateAlertEventHandler StateAlert;
        public void OnStateChange()
        {
            StateChangeEventHandler handler = StateChange;
            if (handler != null)
            {
                handler(this);
            }
        }
        public void OnStateAlert()
        {
            StateAlertEventHandler handler = StateAlert;
            if (handler != null)
            {
                handler(this);
            }
        }

        public Door()
        {
            State = new ClosedState(this);
        }

        public void Open()
        {
            State.Open();
        }

        public void Close()
        {
            State.Close();
        }
    }


External Utilities class used above:

 public class Utilities
    {
        public static AutoResetEvent CallWithTimeout(Action action, int timeoutMilliseconds)
        {
            //Timer stateTimer = new Timer(action, null, 0, 1000);

            var waitHandle = new AutoResetEvent(false);
            ThreadPool.RegisterWaitForSingleObject(
                waitHandle,
                // Method to execute
                (state, timeout) => action(),
                // optional state object to pass to the method
                null,
                // Execute the method after n seconds
                TimeSpan.FromSeconds(timeoutMilliseconds),
                // Execute the method only once. You can set this to false 
                // to execute it repeatedly every 2 seconds
                true);
            return waitHandle;
        }
    }

The above method is responsible for calling the method after n seconds.

Client:

    //Client
    class Program
    {
        static void Main(string[] args)
        {
            Door door = new Door();
            door.StateChange += StateChange;
            door.StateAlert += StateAlert;

            door.Open();
            //Output:Door is opening


            //Let's try opening it again
            door.Open();
            //Output:Door is already opening

            //Let's close it 
            door.Close();
            //Output: Door is closing
            //and after 5 seconds 
            //Output: Door is closed

            //Let's close it again
            door.Close();
            //Output: Door is already closing
            System.Console.ReadLine();
        }

        private static void StateChange(Door door)
        {
            System.Console.WriteLine(door.Status);
        }
        private static void StateAlert(Door door)
        {
            System.Console.WriteLine(door.Status);
        }
    }

The above client will call the context and call the methods on the door object. StateChange and StateAlerts events are hooked so as to perform some action (in this case just printing the status) when state of the door has been changed or some alert has been notified.

Complete Sample:

using System;
using System.Threading;

namespace DesignPatterns.Console
{
    //Client
    class Program
    {
        static void Main(string[] args)
        {
            Door door = new Door();
            door.StateChange += StateChange;
            door.StateAlert += StateAlert;

            door.Open();
            //Output:Door is opening


            //Let's try opening it again
            door.Open();
            //Output:Door is already opening

            //Let's close it 
            door.Close();
            //Output: Door is closing
            //and after 5 seconds 
            //Output: Door is closed

            //Let's close it again
            door.Close();
            //Output: Door is already closing
            System.Console.ReadLine();
        }

        private static void StateChange(Door door)
        {
            System.Console.WriteLine(door.Status);
        }
        private static void StateAlert(Door door)
        {
            System.Console.WriteLine(door.Status);
        }
    }

    public abstract class DoorState
    {
        protected Door Door { get; set; }
        protected int TimeoutInterval { get; set; }
        public abstract void Open();
        public abstract void Close();

        public AutoResetEvent TimeoutCallBackHandler { get; set; }
    }

    //Concrete implementations of DoorState

    public class OpeningState : DoorState
    {
        public OpeningState(Door door)
        {
            this.Door = door;
            this.TimeoutInterval = 5;

            //Door will open after n number of seconds
            this.TimeoutCallBackHandler = Utilities.CallWithTimeout(() =>
                                                                 {
                                                                     if (this.TimeoutCallBackHandler == null) return;
                                                                     this.TimeoutCallBackHandler=null;
                                                                     this.Door.State = new OpenedState(this.Door);
                                                                     this.Door.Status = "Door is opened.";
                                                                     this.Door.OnStateChange();
                                                                 }, this.TimeoutInterval);
        }
        public override void Open()
        {
            this.Door.Status = "Door is already opening.";
            this.Door.OnStateAlert();
        }

        public override void Close()
        {
            this.TimeoutCallBackHandler=null;
            this.Door.State = new ClosingState(this.Door);
            this.Door.Status = "Door is closing.";
            this.Door.OnStateChange();
        }
    }

    public class ClosingState : DoorState
    {
        public ClosingState(Door door)
        {
            this.Door = door;
            this.TimeoutInterval = 5;

            //Door will close after n number of seconds
            this.TimeoutCallBackHandler=Utilities.CallWithTimeout(() =>
            {
                if (this.TimeoutCallBackHandler == null) return;
                this.TimeoutCallBackHandler=null;
                this.Door.State = new ClosedState(this.Door);
                this.Door.Status = "Door is closed.";
                this.Door.OnStateChange();
            }, this.TimeoutInterval);
        }

        public override void Open()
        {
            this.TimeoutCallBackHandler=null;
            this.Door.State = new OpeningState(this.Door);
            this.Door.Status = "Door is opening.";
            this.Door.OnStateChange();
        }

        public override void Close()
        {
            this.Door.Status = "Door is already closing.";
            this.Door.OnStateAlert();
        }

    }

    public class ClosedState : DoorState
    {
        public ClosedState(Door door)
        {
            this.Door = door;
        }
        public override void Open()
        {
            this.TimeoutCallBackHandler=null;
            this.Door.State = new OpeningState(this.Door);
            this.Door.Status = "Door is opening.";
            this.Door.OnStateChange();
        }

        public override void Close()
        {
            this.Door.Status = "Door is already closed.";
            this.Door.OnStateAlert();
        }
    }

    public class OpenedState : DoorState
    {
        public OpenedState(Door door)
        {
            this.Door = door;
            this.TimeoutInterval = 10;

            //After n number of seconds start closing the door by itself
            this.TimeoutCallBackHandler = Utilities.CallWithTimeout(()=>
            {
                if (this.TimeoutCallBackHandler == null) return;
                this.TimeoutCallBackHandler=null;
                this.Door.State = new ClosingState(this.Door);
                this.Door.Status = "Door is closing.";
                this.Door.OnStateChange();
            }, this.TimeoutInterval);
        }
        public override void Open()
        {
            this.Door.Status = "Door is already opened.";
            this.Door.OnStateAlert();
        }

        public override void Close()
        {
            this.TimeoutCallBackHandler=null;
            this.Door.State = new ClosingState(this.Door);
            this.Door.Status = "Door is closing.";
            this.Door.OnStateChange();
        }

    }

    //Context Class
    public class Door
    {
        public DoorState State { get; set; }
        public string Status { get; set; }

        public delegate void StateChangeEventHandler(Door door);
        public delegate void StateAlertEventHandler(Door door);
        public event StateChangeEventHandler StateChange;
        public event StateAlertEventHandler StateAlert;
        public void OnStateChange()
        {
            StateChangeEventHandler handler = StateChange;
            if (handler != null)
            {
                handler(this);
            }
        }
        public void OnStateAlert()
        {
            StateAlertEventHandler handler = StateAlert;
            if (handler != null)
            {
                handler(this);
            }
        }

        public Door()
        {
            State = new ClosedState(this);
        }

        public void Open()
        {
            State.Open();
        }

        public void Close()
        {
            State.Close();
        }
    }

    public class Utilities
    {
        public static AutoResetEvent CallWithTimeout(Action action, int timeoutMilliseconds)
        {
            //Timer stateTimer = new Timer(action, null, 0, 1000);

            var waitHandle = new AutoResetEvent(false);
            ThreadPool.RegisterWaitForSingleObject(
                waitHandle,
                // Method to execute
                (state, timeout) => action(),
                // optional state object to pass to the method
                null,
                // Execute the method after n seconds
                TimeSpan.FromSeconds(timeoutMilliseconds),
                // Execute the method only once. You can set this to false 
                // to execute it repeatedly every 2 seconds
                true);
            return waitHandle;
        }
    }
}

The above example demonstrates the State design pattern in which we saw how the door alters its behaviours based on the states. During runtime it appears as if the type of the object has been changed.

 

Comments (1)

user198172
user198172  said:
Really very professional. helps a lot
2/23/2012
 · 
 
by

Top articles

Quick Vote

What kind of email newsletter would you prefer to receive from CodeAsp.Net?