Click here to Skip to main content
15,884,836 members
Articles / Programming Languages / C#
Article

C# - State Pattern Example

Rate me:
Please Sign up or sign in to vote.
3.25/5 (12 votes)
23 Jun 20052 min read 128.9K   836   53   15
The State Pattern is a common Object Oriented Design Pattern that allows an object to alter its behavior dynamically by changing its internal state.

Introduction

The State Pattern is a common Object Oriented Design Pattern that allows an object to alter its behavior dynamically by changing its internal state. So the State Pattern is a design pattern; what is a design pattern? A design pattern is an example "blueprint" of a solution to solve a common problem. Why use a design pattern? Design patterns are proven and take design constraints and other factors into account.

The State Pattern can be used in many different circumstances. For instance, a car's automatic transmission must behave differently depending on its speed "state". The difference in behavior is represented by the gears or states. In my example, I use a common machine that has a normal operating range, a warning range, and an alert range. The machine will behave differently depending on its range. The objects behavior is delegated to NormalState, WarningState, and AlertState. I represent the difference in behavior with color schemes and text. The color schemes used are:

  1. Green - "Normal"
  2. Yellow - "Warning"
  3. Alert - "Alert"

Image 1

Consider most operating machines; they have a normal, warning, and alert state. For example, a control system "machine" in a power plant such as a Nuclear, Fossil, or Combined Cycle Plant has normal operating ranges and ranges that would trigger an alarm. Operators and/or engineers might want to know the state of certain machines such as a transformer, boiler, etc. While the machine is in operation and running efficiently, it should be in a normal state. If something goes wrong a warning or alert should go off and the users should be alerted. This application demonstrates a simple way of achieving this via the State Pattern.

Class diagram

Image 2

State - Abstract class

This class defines the interface for the derived classes:

C#
using System;
using System.Drawing;

namespace StatePatternApp
{
    /// <summary>
    /// Summary description for State.
    /// </summary>
    public abstract class State
    {
        public State()
        {
        }
        public State(State state)
        {
            this.CurrentLevel    = state.CurrentLevel;
            this.Deviation        = state.Deviation;
            this.MaxLevel        = state.MaxLevel;
            this.MinLevel        = state.MinLevel;
            this.Machine        = state.Machine;
            this.SetParams();
        }
        public State(Machine machine, int iCurrentLevel, 
                int iDeviation, int iMaxLevel, int iMinLevel)
        {
            this.CurrentLevel    = iCurrentLevel;
            this.Deviation        = iDeviation;
            this.MaxLevel        = iMaxLevel;
            this.MinLevel        = iMinLevel;
            this.Machine        = machine;
            this.SetParams();
        }
        private Machine machine;
        private int minLevel = 0;
        private int maxLevel = 0;
        private int currentLevel = 0;
        private int deviation = 0;
        private Color messageColor = Color.Green;
        private string messageText = "";
        public virtual void SetParams(){}
        
        public virtual void CheckEfficiency()
        {
            Transition t = Transition.GetInstance();
            t.Transform(this);
        }

        protected virtual void ChangeState(Machine m, State s)
        {
            m.ChangeState(s);
        }

        public Machine Machine
        {
            get
            {
                return this.machine;
            }
            set
            {
                this.machine = value;
            }
        }
        
        public int MinLevel
        {
            get
            {
                return this.minLevel;
            }
            set
            {
                this.minLevel = value;
            }
        }

        public int MaxLevel
        {
            get
            {
                return this.maxLevel;
            }
            set
            {
                this.maxLevel = value;
            }
        }

        public int CurrentLevel
        {
            get
            {
                return this.currentLevel;
            }
            set
            {
                this.currentLevel = value;
                // Is the machine value set?
                if(this.Machine != null)
                {
                    this.CheckEfficiency();
                }
            }
        }

        public int Deviation
        {
            get
            {
                return this.deviation;
            }
            set
            {
                this.deviation = value;
            }
        }

        public int MaxDeviaton
        {
            get
            {
                return this.MaxLevel - this.Deviation;
            }
        }

        public int MinDeviaton
        {
            get
            {
                return this.MinLevel + this.Deviation;
            }
        }

        public Color MessageColor
        {
            get
            {
                return this.messageColor;
            }
            set
            {
                this.messageColor = value;
            }
        }

        public string MessageText
        {
            get
            {
                return this.messageText;
            }
            set
            {
                this.messageText = value;
            }
        }
    }
}

Transition class

I decided to move the transition logic from each state object to a transition class "Singleton". This allows the application to be more maintainable and scalable. The transition class has a Transform method that determines which state to transition to, if needed. *** Thanks to Marc Clifton for pointing out the redundancy of having the transition logic in each state object.

C#
using System;

namespace StatePatternApp
{
    class Transition
    {
        private static Transition getInstance;
        protected Transition() {}

        
        public static Transition GetInstance()
        {
            if(getInstance == null)
            {
                getInstance = new Transition();
            }
            return getInstance;
        }

        public void Transform(State state)
        {
            if(state == null)
            {
                return;
            }

            // Get the type of state.
            string stateType = state.GetType().Name;

            // Are we in normal state?
            if(state.CurrentLevel < state.MaxDeviaton && 
                     state.CurrentLevel > state.MinDeviaton)
            {
                if(stateType.ToUpper() != "NORMALSTATE")
                {
                    state.ChangeState(state.Machine, 
                                 new NormalState(state));
                }
            }
            // Are we in warning?
            if(state.Deviation > 0)
            {
                if((state.CurrentLevel < state.MaxLevel && 
                      state.CurrentLevel >= state.MaxDeviaton) || 
                    state.CurrentLevel > state.MinLevel && 
                      state.CurrentLevel <= state.MinDeviaton)
                {
                    if(stateType.ToUpper() != "WARNINGSTATE")
                    {
                        state.ChangeState(state.Machine, 
                                     new WarningState(state));
                    }
                }
            }
            // Are we in alert state?
            if(state.CurrentLevel >= state.MaxLevel || 
                        state.CurrentLevel <= state.MinLevel)
            {
                if(stateType.ToUpper() != "ALERTSTATE")
                {
                    state.ChangeState(state.Machine, 
                                   new AlertState(state));
                }
            }
        }
    }
}

NormalState - derived state class

Implements a normal behavior. Displays Green and states "Normal":

C#
using System;
using System.Drawing;

namespace StatePatternApp
{
    /// <summary>
    /// Summary description for NormalState.
    /// </summary>
    public class NormalState : State
    {
        public NormalState(State state) : base(state)
        {
        }

        public NormalState(Machine machine, int iCurrentLevel, 
                   int iDeviation, int iMaxLevel, int iMinLevel) :
                   base(machine, iCurrentLevel, iDeviation, 
                   iMaxLevel, iMinLevel)
        {    
        }

        public override void SetParams()
        {
            this.MessageColor    = Color.Green;
            this.MessageText    = "Normal";
        }
    }
}

WarningState - derived state class

Implements a warning behavior. Displays Yellow and states "Warning":

C#
using System;
using System.Drawing;

namespace StatePatternApp
{
    /// <summary>
    /// Summary description for WarningState.
    /// </summary>
    public class WarningState : State
    {
        public WarningState(State state) : base(state)
        {
        }

        public WarningState(Machine machine, int iCurrentLevel, 
            int iDeviation, int iMaxLevel, int iMinLevel) :
            base(machine, iCurrentLevel, iDeviation, iMaxLevel, 
            iMinLevel)
        {
        }

        public override void SetParams()
        {
            this.MessageColor    = Color.Yellow;
            this.MessageText    = "Warning";
        }
    }
}

AlertState - derived state class

Implements an alert behavior. Displays Red and states "Alert":

C#
using System;
using System.Drawing;

namespace StatePatternApp
{
    /// <summary>
    /// Summary description for AlertState.
    /// </summary>
    public class AlertState : State
    {
        public AlertState(State state) : base(state)
        {
        }

        public AlertState(Machine machine, int iCurrentLevel, 
            int iDeviation, int iMaxLevel, int iMinLevel) :
            base(machine, iCurrentLevel, iDeviation, iMaxLevel, 
            iMinLevel)
        {
        }

        public override void SetParams()
        {
            this.MessageColor    = Color.Red;
            this.MessageText    = "Alert";
        }
    }
}

Machine class

This class maintains an instance of state:

C#
using System;

namespace StatePatternApp
{
    /// <summary>
    /// Summary description for Machine.
    /// </summary>
    public class Machine
    {
        public State currentState;

        public Machine(int iCurrentLevel, int iDeviation, 
                               int iMaxLevel, int iMinLevel)
        {
            currentState = new NormalState(this, iCurrentLevel, 
                               iDeviation, iMaxLevel, iMinLevel);
            currentState.CheckEfficiency();
        }

        public void ChangeState(State setState)
        {
            currentState = setState;
        }

        public void SetCurrentLevel(int level)
        {
            currentState.CurrentLevel = level;
        }
    }
}

Now that you have seen an example of the State Pattern, you may have probably realized many places where you can implement this. Although the State Pattern is very powerful, it can be overly complex in some situations and in such situations flags or polymorphism could be used instead. The State Pattern may have an initial impact upfront but is more maintainable and more productive over time.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
GeneralI disagree with the Transition class Pin
Mark Graham19-Feb-09 3:16
Mark Graham19-Feb-09 3:16 
Questioncould you help me to implement state pattern Pin
nallanch_srinivas26-Nov-07 10:49
nallanch_srinivas26-Nov-07 10:49 
GeneralState in XML Pin
Member 207692927-Jun-05 11:05
Member 207692927-Jun-05 11:05 
GeneralRe: State in XML Pin
Kenneth Young27-Jun-05 11:18
Kenneth Young27-Jun-05 11:18 
QuestionWhy is state logic is replicated? Pin
Marc Clifton24-Jun-05 3:20
mvaMarc Clifton24-Jun-05 3:20 
AnswerRe: Why is state logic is replicated? Pin
Kenneth Young24-Jun-05 3:54
Kenneth Young24-Jun-05 3:54 
AnswerRe: Why is state logic is replicated? Pin
Kenneth Young24-Jun-05 4:24
Kenneth Young24-Jun-05 4:24 
GeneralRe: Why is state logic is replicated? Pin
Marc Clifton24-Jun-05 5:01
mvaMarc Clifton24-Jun-05 5:01 
GeneralRe: Why is state logic is replicated? Pin
Kenneth Young24-Jun-05 5:32
Kenneth Young24-Jun-05 5:32 
GeneralRe: Why is state logic is replicated? Pin
Marc Clifton24-Jun-05 6:10
mvaMarc Clifton24-Jun-05 6:10 
GeneralSeparation of concerns Pin
Marc Clifton23-Jun-05 16:43
mvaMarc Clifton23-Jun-05 16:43 
GeneralRe: Separation of concerns Pin
Kenneth Young24-Jun-05 2:46
Kenneth Young24-Jun-05 2:46 
GeneralRe: Separation of concerns Pin
Marc Clifton24-Jun-05 3:10
mvaMarc Clifton24-Jun-05 3:10 
GeneralRe: Separation of concerns Pin
Kenneth Young24-Jun-05 3:56
Kenneth Young24-Jun-05 3:56 
GeneralRe: Separation of concerns Pin
Marc Clifton24-Jun-05 4:47
mvaMarc Clifton24-Jun-05 4:47 

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

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