What Is a State Machine?Anyone who has taken courses in digital logic design will be intimately familiar with the concept of a "state machine." This book uses a simplified version of this state machine definition. If you are not yet familiar with state machines, the concept is relatively simple and you probably already intuitively understand how state machines work. The "state" of your application can be defined as the aggregate of all of its variables. The values of all of an application's variables give it a unique state that will only be changed when some external event causes a change. Examples of these kinds of state-changing events are events such as a user pressing a key, a button being clicked, an event firing due to an incoming message, or even a timer event. At any given moment, your application is in a certain state. It will enter a new state when something perturbs that state. The state that your application is currently in plus the new input are used to determine the next state. A state machine is a formal structuring of this reality. Instead of using all of the application variables as a broad definition of the application's state, a state machine creates a single variable and defines it as the holder of some important aspect of the application's state. This variable is usually a global or class-level integer enumeration (for example, int m_myApplicationState;) with a defined set of valid states. The great strength of the state machine approach is that it enables you to come up with an explicit definition of valid states for some aspect of your application and enforce proper behaviors as an application moves from one state to another. An application can have more than one state machine with each separate state machine governing some useful set of behaviors that need to be structured. An application without any state machines is really just an application with a great many state machines; each application-level variable effectively is its own state machine and all kinds of code can access and modify this state. State machines are used to take a set of related variables or behaviors and organize them in a logical and maintainable way. State machines = organized behavior. Figure 5.1 shows a state machine schematic for a multiple-choice vocabulary teaching game. This example is used several times in this chapter. The application's behavior has been broken down into a few logical states. These logical states exist whether or not we choose to define them in a state machine. The state machine is just a formal definition of how the application operates. It enables us to think crisply about the different states the application can move through as the user interacts with it. Figure 5.1. A state machine for a simple multiple-choice vocabulary game.[View full size image] ![]() Another way of viewing the different states of the application is by building a state transition table. This table lists the discrete states that the application can exist in and shows which state transitions are possible. Table 5.1 shows the list of states and transitions for our application.
Note I have chosen to list all the permutations of State and External Inputs above to illustrate that not all state transitions need be valid. Rows above with the Next State marked as "Illegal state transition" are not permissible in our application. If the application somehow gets into one of these state transitions, some of the application logic is faulty. The state machine logic should throw an exception or at least raise a debugger assert if an invalid state transition is encountered. Identifying illegal state transitions can aid you in debugging your applications. Listing 5.1 realizes in code the state machine defined above. The code should bear a close resemblance to the state transitions defined in Table 5.1 and Figure 5.1. In the function below, you will notice that the code for each of the state transitions has a commented-out function call. This function call represents work to do for the state transition and is commented out to allow the code below to compile as a self-contained unit; the implementation of the function calls is left to you. It is a useful design pattern to define your state transitions as a switch/case statement block. Each "case" statement represents a state transition and should call a function to perform any work necessary to accomplish the state transition. This kind of centralization and encapsulation of state management is the most powerful thing about state machines; you have a central place where all of the important application transitions are defined and processed. Listing 5.1. Sample Code for State Machine for Multiple-Choice Game
|