State Machine Inheritance
We have already seen how the traditional state machine can be transformed into a hierarchical state machine by defining a base state and inheriting from it. In this article, we will go a step further and explore ways of inheriting from the state machine itself.
What is State Machine Inheritance?
State Machine inheritance provides a powerful way of teaching new tricks to an old state machine. Consider a IncomingCall and OutgoingCall objects in a call processing implementation. A lot of functionality between the two call types is shared. This common functionality could be implemented in a common Call object. IncomingCall and OutgoingCall can inherit from the Call object. The inheritance tree can be refined by defining call objects based on the signaling type. This would result in objects like V52IncomingCall, ISUPOutgoingCall etc. Since each of these object is a state machine, we have to consider ways by which state machine inheritance can be implemented.
Another example could be Unit state machine discussed in the hierarchical state machine article. Unit could be the base class for different types of hardware units in a system. TransducerUnit, ProcessorUnit, TerminalUnit could all inherit from Unit and extend the state machine defined by it.
We will use the Unit state machine example, to discuss the following inheritance techniques:
This technique involves overriding the methods of the base class to provide the required functionality in the deriving class. Here, the state sequencing is completely defined by the base class, the inheriting classes merely refine the methods invoked in that state machine.
The following example uses this technique. The ProcessorUnit inherits from Unit class and overrides the methods SendDiagnosticsRequest, PerformSwitchover and SendSwitchoverResponse.
In this technique, the states defined by the base class can be overridden by inheriting class. The inheriting class provides plug in replacements for the base class states. The main objective of overriding the states is to enhance functionality of the state. The inherited states take care of specific handling for the messages in the state. Generally the state sequence defined by the base class is preserved.
To allow overriding the states of the base class, states are accessed by pointers. These pointers are initialized to state objects defined in the class. The inheriting state machine class can derive from the base class states and modify the pointer appropriately.
In the example give below, Unit state machine object has been modified to access all states through pointers. The Unit constructor initializes these pointers to the appropriate states. The deriving TerminalUnit class overrides the Active, Standby and Suspect states of the Unit class. The constructor initializes the pointers to these states to the derived states, TerminalActiveState, TerminalStandbyState and TerminalSuspectState.
Dividing Into Sub-States
In this case too, the states of the base class are overridden to enhance functionality. The difference is that one base class state may map to many derived class sub-states. The pointer is initialized to one of the derived sub-states, referred to as the triggering sub-state. The state transitions to other derived sub-states are initiated by the triggering sub-state. Even though the sub-states have been introduced in the derived class, the base class state machine sequencing is still preserved. From the base class's perspective, it transitions to one state, the inheriting class performs sub-state transitions without the knowledge of the base class.
In the example given below, the Suspect states of TransducerUnit are shown. The transducer diagnostics tests generally involve setting up the test, running the test and finally dismantling the test. Thus the suspect state needs to be broken down into three sub-state. These sub-states are TransducerTestSetupState, TransducerTestRunningState and TransducerTestDismantleState. These sub-states derive from the Suspect state of the Unit base class. The base class's Suspect state pointer is initialized to the triggering class, TransducerTestSetupState in the constructor. The transitions to other Suspect sub-states are triggered from this class. Finally, the TransducerUnit state machine transitions out to a state supported by the Unit base class.