Introduction
This article presents a C# implementation of the Gang of Four State Pattern using features like Generics, enumeration, and Reflection to make life easier. The reader is assumed to be familiar with UML State Charts and the Gang of Four State Pattern; this article is not intended as a tutorial on those subjects.
Motivation
I regularly use UML State Charts to design state behavior. UML State Charts are very useful in discussions with users. When it comes to implementing, I like to use the Gang of Four State Pattern. The pattern enables a clear translation of a State Chart into code. But a couple of things annoyed me with the standard implementations:
- StateContext needs to be updated in several places when adding concrete states.
- Intellisense/auto-complete is not really helpful when setting a property of abstract type.
- Concrete states need constructors with a reference to the StateContext. This contradicts with the clear translation of State Chart into code, and is distracting for the casual reader, especially while state classes can be very small.
These issues are addressed in a neat little package called fsm4net. Let's have a look.
Example
Let's dive in. Suppose we need to implement the following State Chart. We have a light that we need to turn on and off with a toggle event. And furthermore, the light should be turned off after a timeout. This simple example uses states, transitions, triggers, actions, and timeout.
Using fsm4net, the implementation of our concrete states looks like this (actual code):
enum States { OnState, OffState, EmptyState }
class OffState : StateBase
{
public override void EntryAction()
{
Context.Light.Off();
}
protected override void OnToggle()
{
Context.Next = States.OnState;
}
}
class OnState : StateBase
{
public override void EntryAction()
{
Context.Timeout = TimeSpan.FromSeconds(3);
Context.Light.On();
}
protected override void OnToggle()
{
Context.Next = States.OffState;
}
public override void TimeoutHandler()
{
Context.Next = States.OffState;
}
}
What I really like about this code is that it translates very well to the State Chart and is also understandable for non-programmers. If you like it so far, then keep reading.
Overview
We'll take a look at the big picture first and then we'll get back to the example.
This class diagram shows what you get when using fsm4net, i.e., inherit the abstract base classes and interface of the fsm4net assembly. The classes will look familiar if you've seen the State Pattern before. Let's go over them:
StatesEnum
is an enumeration that identifies the concrete states in your machine and the empty or final state that is used for termination. It is very convenient while entering state change code to be able to select values from an enum using intellisense/autocomplete.
enum StatesEnum { ConcreteState1, ConcreteState2, Empty }
StateBase
is the abstract base class for all your concrete states. The simplest form is:
abstract class StateBase : State<statecontext,>
{
}
StateBase
is typically expanded with virtual handlers for specific triggers, as we'll see in the example later on.
StateContext
is the bookkeeper. The base constructor takes two parameters: the enum values of the first state and the final state. The simplest form is:
interface IStateContext : IStateContext<statesenum> { }
class StateContext : StateContext<statesenum>, IStateContext
{
public StateContext()
: base(StatesEnum.ConcreteState1, StatesEnum.Empty)
{
}
}
StateContext
can be reached from every state using their inherited Context
property. Typically, StateContext
is expanded with properties that allow for either persisting data across states or accessing interfaces from states (Light
, in our example).
IstateContext
provides an interface for the thread that runs the state machine. The simplest implementation looks like:
IMyStateContext machine = new MyStateContext();
while (machine.IsActive)
{
machine.Handle();
}
Every time Handle()
is called, it will either:
- Switch state (when a Next state is set) (calling
ExitAction()
on the current state, and EntryAction()
on the new state) - Notify timeout (when timeout is elapsed) (calling
TimeoutHandler()
on the current state) - Notify trigger (when a trigger event is queued) (calling
TriggerHandler()
on the current state) - Otherwise: Call
DoAction()
on the current state
An example of a simple concrete state:
class ConcreteState1 : StateBase
{
public override void EntryAction()
{
Context.Next = States.EmptyState;
}
}
Look mum, no constructor (distractor)!
Details
The base constructor of StateContext
performs all the magic, and uses Reflection to find out which states are implemented; instantiates the concrete states and matches them to StatesEnum
. Some rules for the definition of states apply:
- Every concrete state needs a corresponding enum value of the exact same name.
- One extra enum value needs to be defined for the exit or terminating state, and cannot have an associated class.
- No more rules :-)
StateContext
contains an EventArgs
Queue to allow for thread-safe handling of events / triggers.
void EnqueueTriggerEventHandler(object sender, EventArgs e);
The enqueue method has the EventHandler
signature, and can therefore directly subscribe to events. The sender
object will be discarded.
StateContext
contains a Timeout
property of type TimeSpan
that can be used for state timeouts. Timeouts are typically set in the EntryAction
of a concrete state. Timeouts are reset when state is changed. When a timeout occurs, the TimeoutHandler()
on the current state is called. The default action is to throw a NotImplementedException("Unhandled timeout")
.
Back to the example
We have already seen the concrete state implementations of our example state chart. Now, let's go over the other classes.
IStateContext
is the interface for the thread that runs the state machine. All members are inherited:
interface IStateContext : IStateContext<statesenum /> { }</statesenum />
StateContext
publishes the ILight
interface so states can control the light. Furthermore, StateContext
subscribes to the OnToggle
events:
class StateContext : StateContext<states>, IStateContext
{
private ILight m_Light;
public StateContext(ILight light)
: base(States.OffState, States.EmptyState)
{
light.OnToggle += new EventHandler(EnqueueTriggerEventHandler);
m_Light = light;
}
public ILight Light
{
get { return m_Light; }
}
}
And finally, StateBase
will translate the triggers into OnToggle
calls.
abstract class StateBase : State<statecontext,>
{
public override void TriggerHandler(EventArgs e)
{
OnToggle();
}
protected virtual void OnToggle() { }
}
Our example is very simple. Typically, TriggerHandler
will check the type of the argument and possibly its properties to distribute the events to separate method calls.
Conclusion
fsm4net provides a neat way to implement UML State Charts in C# using the GoF State Pattern. There is a good separation between functionality and implementation details, and intellisense / auto-complete really helps with the coding of the states. I hope it will be useful to you too.
History