Click here to Skip to main content
11,430,274 members (73,907 online)
Click here to Skip to main content
Technical Blog

State Design Pattern

, 14 Aug 2009 CPOL
Rate this:
Please Sign up or sign in to vote.
State Design Pattern

Introduction

In state design pattern, it can change its behaviour by switching to a set of different operations and we achieved this pattern from an object variable changing its subclass, within a hierarchy. Allow an object to alter its behaviour when its internal state changes. The object will appear to change its class.

Explanation with Shopping Cart Example

The State pattern has some Mediator design pattern aspects to it. To understand more about state design patterns, let's look in more detail of an example that has been mentioned in many books and many blogs but I myself could not find its actual and true implementation. Assume you have a sales order that can be in several different states, such as "NewOrder," "Registered," "Granted," "Shipped," "Invoiced," and "Cancelled." There are strict rules concerning which states the order can "go," and from which states it can "come." In my example, for instance, the states not allowed to go directly are from Registered to Shipped. This example is from Applying Domain-Driven Design and Patterns: With examples in C# and .NET [Addison-Wesley Professional, ISBN: 0321268202] by Jimmy Nilsson. However there was no code implementation of it.

There are also differences in behaviour depending upon the states. For example, when order is dispatched, you can't call registered or approved or cancel order for adding more items to the order. Always remember that certain behaviour leads to state transformation. UML diagram of state design pattern is:

UML Diagram

image

In our example:

Context = IOrderState , 
State = OrderState 
Concrete State = (NewOrder, Registered, Dispatched, Approved)

SO we have an interface called IOrderState which manages all the states of an order. I have kept it to only 4 different states in my example as below:

public interface IOrderState
        {
            void NewOrderPlaced();
            void Register();
            void Dispatch();
            void Approve();
        } 

Then we have four concrete state classes which implement IOrderState interface which are NewOrder, Registered, Dispatched, Approved as given below:

 public class NewOrder : IOrderState
        {
            private readonly OrderState _Parent;
            public NewOrder(OrderState OrderState)
            {
                _Parent = OrderState;
                this.NewOrderPlaced();
               
            }
            public bool IsDispatched
            {
                get { return false; }
            }
            public void NewOrderPlaced()
            {
                Console.WriteLine("NewOrderPlaced");
            }
            public void Dispatch()
            {
                _Parent._CurrentState = new Dispatched(_Parent);
            }
            public void Register()
            {
                _Parent._CurrentState = new Registered(_Parent);
            }
            public void Approve()
            {
                _Parent._CurrentState = new Approved(_Parent);
            }
        }
        public class Registered : IOrderState
        {
            private readonly OrderState _Parent;
            public void NewOrderPlaced()
            {
                throw new Exception("OrderState has already been placed");
            }
            public Registered(OrderState OrderState)
            {
                _Parent = OrderState;
                this.Register();
            }
            public void Dispatch()
            {
                throw new Exception("OrderState has not been registered yet");
            }
            public void Register()
            {
                Console.WriteLine("Registered");
            }
            public void Approve()
            {
                _Parent._CurrentState = new Approved(_Parent);
            }
        }
        public class Approved : IOrderState
        {
            private readonly OrderState _Parent;
            public Approved(OrderState OrderState)
            {
                _Parent = OrderState;
                this.Approve();
            }
            public void NewOrderPlaced()
            {
                throw new Exception("OrderState has already been placed");
            }
            public void Dispatch()
            {
                _Parent._CurrentState = new Dispatched(_Parent);
            }
            public void Register()
            {
                throw new Exception("OrderState has already been registered");
            }
            public void Approve()
            {
                Console.WriteLine("Approved");
            }
        }
        public class Dispatched : IOrderState
        {
            private readonly OrderState _Parent;
            public void NewOrderPlaced()
            {
                throw new Exception("OrderState has already been placed");
            }
            public Dispatched(OrderState OrderState)
            {
                _Parent = OrderState;
                this.Dispatch();
            }
            public void Dispatch()
            {
                Console.WriteLine("dispatched");
            }
            public void Register()
            {
                throw new Exception("OrderState has already been registered");
            }
            public void Approve()
            {
                _Parent._CurrentState = new Approved(_Parent);
            }
        } 

Then we have an OrderState class which is a context class that manages all the states of a class as below:

public class OrderState
        {
            public IOrderState _CurrentState;
            public OrderState()
            {
                _CurrentState = new NewOrder(this);
            }
            public void Dispatch()
            {
                _CurrentState.Dispatch();
            }
            public void Register()
            {
                _CurrentState.Register();
            }
            public void Approve()
            {
                _CurrentState.Approve();
            }
        } 

to use it:

static void Main(string[] args)
        {
            OrderState OrderState = new OrderState();
            OrderState.Register();
            OrderState.Approve();
            OrderState.Dispatch();
        } 

and the output is:

image

Note

Now the beauty of this pattern is once you have OrderState.Dispatch(); you cannot user OrderState.Register(); it will throw an exception that OrderState has already been registered. So it also removes so many if/else statements because it switches to a different class depending on its state.

Things to Remember

  • Using state design pattern, the program can potentially be flooded with small classes.
  • Each derived or small class has knowledge of (coupling to) its siblings, which introduces dependencies between subclasses.
  • The state pattern requires tight coupling between its classes.
  • State Design Pattern somehow solves your problem with the least amount of duplicated code and also the responsibility portioned out into encapsulated and cohesive unites, the concrete state classes.
  • State has no state usually it selects the next state of its context. A state tends to have lots of unrelated methods, so there is little cohesion between the methods of a state.

Difference between Strategy Design Pattern and State Design Pattern.

Strategy pattern is similar to the State Design Pattern from an architectural point of view but the intent is entirely different. Unlike the State pattern, the Algorithm class doesn’t manage state, but represents an algorithm that uses one or more IStrategy implementations. The IStrategy implementation isn’t managed by the Algorithm class, but assigned by the client whereas a state usually selects the next state of its context. A state tends to have lots of unrelated methods, so there is little cohesion between the methods of a state. For more details, please read my post on Strategy Design Pattern.

To view this and other design patterns on my blog, please click.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Salmanzz
Software Developer (Senior) BMJ
United Kingdom United Kingdom
No Biography provided

Comments and Discussions

 
Generalviolates ocp Pin
Member 1049125813-Mar-15 9:08
memberMember 1049125813-Mar-15 9:08 
GeneralVote 4-> Excellent Example Pin
Rupesh Kumar Tiwari2-Apr-14 5:05
memberRupesh Kumar Tiwari2-Apr-14 5:05 
QuestionState pattern using Loan states as an example Pin
Nilesh Gule12-Jul-12 6:38
memberNilesh Gule12-Jul-12 6:38 
GeneralMy vote of 3 Pin
Member 292366024-Aug-10 14:37
memberMember 292366024-Aug-10 14:37 
GeneralCorrection Pin
Member 292366024-Aug-10 5:13
memberMember 292366024-Aug-10 5:13 
GeneralMy vote of 5 Pin
cysk28-Jul-10 22:38
membercysk28-Jul-10 22:38 
GeneralGood article Pin
HightechRider14-Aug-09 9:27
memberHightechRider14-Aug-09 9:27 
GeneralRe: Good article Pin
Salmanzz14-Aug-09 9:33
memberSalmanzz14-Aug-09 9:33 
GeneralOh, the GoF State Pattern! Pin
Axel Rietschin14-Aug-09 8:15
memberAxel Rietschin14-Aug-09 8:15 
Good article. The origial book (to my knowledge) where this pattern was described is Design Patterns: Elements of Reusable Object-Oriented Software, Gamma, Helm, Johnson, Vlissides (aka the Gang of Four, aka GoF), Addison-Wesley 1994, ISBN 978-0201633610.

One can find many code examples by googling Gof State Pattern, even a C# state machine code generator template, working with the freeware version of CodeSmith.
GeneralRe: Oh, the GoF State Pattern! Pin
Salmanzz14-Aug-09 9:32
memberSalmanzz14-Aug-09 9:32 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web01 | 2.8.150428.2 | Last Updated 14 Aug 2009
Article Copyright 2009 by Salmanzz
Everything else Copyright © CodeProject, 1999-2015
Layout: fixed | fluid