[ Team LiB ] Previous Section Next Section

Implementing State Diagrams

A state diagram can be implemented in three main ways: nested switch, the State pattern, and state tables. The most direct approach to handling a state diagram is a nested switch statement, such as Figure 10.6. Although it's direct, it's long-winded, even for this simple case. It's also very easy for this approach to get out of control, so I don't like using it even for simple cases.

Figure 10.6 A C# nested switch to handle the state transition from Figure 10.1
public void HandleEvent (PanelEvent anEvent) {
  switch (CurrentState) {
    case PanelState.Open :
      switch (anEvent) {
        case PanelEvent.SafeClosed :
          CurrentState = PanelState.Wait;
          break;
      }
      break;
    case PanelState.Wait :
      switch (anEvent) {
        case PanelEvent.CandleRemoved :
          if (isDoorOpen) {
            RevealLock();
            CurrentState = PanelState.Lock;
          }
          break;
      }
      break;
    case PanelState.Lock :
      switch (anEvent) {
        case PanelEvent.KeyTurned :
          if (isCandleIn) {
            OpenSafe();
            CurrentState = PanelState.Open;
          } else {
            ReleaseKillerRabbit();
            CurrentState = PanelState.Final;
          }
          break;
      }
      break;
   }
  }
}

The State pattern [Gang of Four] creates a hierarchy of state classes to handle behavior of the states. Each state in the diagram has one state subclass. The controller has methods for each event, which simply forwards to the state class. The state diagram of Figure 10.1 would yield an implementation indicated by the classes of Figure 10.7.

Figure 10.7. A State pattern implementation for Figure 10.1

graphics/10fig07.gif

The top of the hierarchy is an abstract class that implements all the event-handling methods to do nothing. For each concrete state, you simply override the specific event methods for which that state has transitions.

The state table approach captures the state diagram information as data. So Figure 10.1 might end up represented in a table like Table 10.1. We then build either an interpreter that uses the state table at runtime or a code generator that generates classes based on the state table.

Obviously, the state table is more work to do once, but then you can use it every time you have a state problem to hold. A runtime state table can also be modified without recompilation, which in some contexts is quite handy. The state pattern is easier to put together when you need it, and although it needs a new class for each state, it's a small amount of code to write in each case.

These implementations are pretty minimal, but they should give you an idea of how to go about implementing state diagrams. In each case, implementing state models leads to very boilerplate code, so it's usually best to use some form of code generation to do it.

Table 10.1. A State Table for Figure 10.1

Source State

Target State

Event

Guard

Procedure

Wait

Lock

Candle removed

Door open

Reveal lock

Lock

Open

Key turned

Candle in

Open safe

Lock

Final

Key turned

Candle out

Release killer rabbit

Open

Wait

Safe closed

  

    [ Team LiB ] Previous Section Next Section