State
The State pattern allows an object to change it behaviour when its internal state changes.
The pattern is a type of behavioral design pattern, and is also known as the objects for states pattern. This pattern is used in programming to represent the state of an object. This is a way for an object to partially change its type at runtime
A VB example of the State Pattern
This example shows the different states a person would go through after drinking beer, a bit crude but I hope it demostrates what the state pattern is.
The below code gives this output:
I have had 0 cans of beer and I am feeling sober.
*hic* I have had 6 cans of beer and I am feeling a bit tipsy :0).
*hic* I have had 14 cans of beer and I am feeling a very, very drunk :0).
Dim aPerson As New Person
Response.Write(String.Format("{0}<br/>", aPerson.DrunkState.displayState))
' Have a beer
aPerson.Drink(6)
Response.Write(String.Format("{0}<br/>", aPerson.DrunkState.displayState))
' Have some more beers
aPerson.Drink(3)
aPerson.Drink(5)
Response.Write(String.Format("{0}<br/>", aPerson.DrunkState.displayState))
' Person object
Public Class Person
Private _CansOfBeerDrunk As Integer = 0
' The state of the person
Private _DrunkState As DrunkState
Public Sub New()
' Default state is sober
_DrunkState = New Sober(0, Me)
End Sub
Public Sub Drink(ByVal numberOfCans As Integer)
_CansOfBeerDrunk += numberOfCans
_DrunkState.AmountDrunk(_CansOfBeerDrunk)
End Sub
Public Property DrunkState() As DrunkState
Get
Return _DrunkState
End Get
Set(ByVal value As DrunkState)
_DrunkState = value
End Set
End Property
End Class
' Abstract Base State Class
Public MustInherit Class DrunkState
Protected _person As Person
Protected _numberOfCans As Integer
MustOverride Sub AmountDrunk(ByVal NumberOfCans As Integer)
MustOverride Function displayState() As String
Public ReadOnly Property NumberOfCansDrunk() As Integer
Get
Return _numberOfCans
End Get
End Property
Public ReadOnly Property Person() As Person
Get
Return _person
End Get
End Property
End Class
' Sober State
Public Class Sober
Inherits DrunkState
Public Sub New(ByVal State As DrunkState)
Me._numberOfCans = State.NumberOfCansDrunk
Me._person = State.Person
End Sub
Public Sub New(ByVal NumberOfCans As Integer, ByVal aPerson As Person)
Me._numberOfCans = NumberOfCans
Me._person = aPerson
End Sub
Public Overrides Sub AmountDrunk(ByVal NumberOfCans As Integer)
_numberOfCans = NumberOfCans
CheckOnStateOfPerson()
End Sub
Private Sub CheckOnStateOfPerson()
' Check to see if the number of cans will change our persons
' state
If Me._numberOfCans > 5 Then
_person.DrunkState = New Tipsy(Me)
ElseIf Me._numberOfCans > 10 Then
_person.DrunkState = New DrunkAsASkunk(Me)
End If
End Sub
Public Overrides Function displayState() As String
Return String.Format("I have had {0} cans of beer and I am feeling sober.", Me._numberOfCans)
End Function
End Class
' Tipsy State
Public Class Tipsy
Inherits DrunkState
Public Sub New(ByVal State As DrunkState)
Me._numberOfCans = State.NumberOfCansDrunk
Me._person = State.Person
End Sub
Public Sub New(ByVal NumberOfCans As Integer, ByVal aPerson As Person)
Me._numberOfCans = NumberOfCans
Me._person = aPerson
End Sub
Public Overrides Sub AmountDrunk(ByVal NumberOfCans As Integer)
Me._numberOfCans = NumberOfCans
CheckOnStateOfPerson()
End Sub
Private Sub CheckOnStateOfPerson()
' Check to see if the number of cans will change our persons
' state
If Me._numberOfCans < 5 Then
Me._person.DrunkState = New Sober(Me)
ElseIf Me._numberOfCans > 10 Then
Me._person.DrunkState = New DrunkAsASkunk(Me)
End If
End Sub
Public Overrides Function displayState() As String
Return String.Format("*hic* I have had {0} cans of beer and I am feeling a bit tipsy :0).", Me._numberOfCans)
End Function
End Class
' Very Drunk State
Public Class DrunkAsASkunk
Inherits DrunkState
Public Sub New(ByVal State As DrunkState)
Me._numberOfCans = State.NumberOfCansDrunk
Me._person = State.Person
End Sub
Public Sub New(ByVal NumberOfCans As Integer, ByVal aPerson As Person)
Me._numberOfCans = NumberOfCans
Me._person = aPerson
End Sub
Public Overrides Sub AmountDrunk(ByVal NumberOfCans As Integer)
Me._numberOfCans = NumberOfCans
CheckOnStateOfPerson()
End Sub
Public Overrides Function displayState() As String
Return String.Format("*hic* I have had {0} cans of beer and I am feeling a very, very drunk :0).", Me._numberOfCans)
End Function
Private Sub CheckOnStateOfPerson()
' Check to see if the number of cans will change our persons
' state
If Me._numberOfCans < 5 Then
Me._person.DrunkState = New Sober(Me)
ElseIf Me._numberOfCans < 10 Then
Me._person.DrunkState = New Tipsy(Me)
End If
End Sub
End Class
A C# example of the State Pattern
#region State
public interface IState
{
void Handle(StateContext context);
}
#endregion
#region Context
public class StateContext
{
#region Properties
/// <summary>
/// The current inner state
/// </summary>
public IState State { get; set; }
/// <summary>
/// An inner counter of this context
/// </summary>
public int Counter { get; set; }
#endregion
#region Ctor
/// <summary>
/// Construct a new StateContext with the
/// given first state
/// </summary>
/// <param name="state">The first state of the object</param>
public StateContext(IState state)
{
State = state;
}
#endregion
#region Methods
/// <summary>
/// Send a request to be handled by the inner state
/// behavior
/// </summary>
public void Request()
{
State.Handle(this);
}
#endregion
}
#endregion
#region Concrete State
public class ConcreteStateA : IState
{
#region IState Members
public void Handle(StateContext context)
{
context.Counter += 2;
// change the context state to a new state
context.State = new ConcreteStateB();
}
#endregion
}
public class ConcreteStateB : IState
{
#region IState Members
public void Handle(StateContext context)
{
context.Counter -= 1;
// change the context state to a new state
context.State = new ConcreteStateA();
}
#endregion
}
#endregion
The implementation of state depends on inheritance. The main issues is to
understand that every concrete state is responsible to change the current
context behavior to the next context behavior in the chain of behaviors. The
example I gave is simple and can be run with this code:
// initialize a context with a first state behavior
StateContext context =
new StateContext(new ConcreteStateA());
context.Request();
Console.WriteLine("The current counter need to be 2: {0}",
context.Counter);
context.Request();
Console.WriteLine("The current counter need to be 1: {0}",
context.Counter);
context.Request();
Console.WriteLine("The current counter need to be 3: {0}",
context.Counter);
Console.Read();
Articles