I use state machines and SMC for several classes of application. High-Level Application Policies for GUIsOne of the goals of the graphical revolution in the 1980s, was to create stateless interfaces for humans to use. At that time, computer interfaces were dominated by textual approaches using hierarchical menus. It was easy to get lost in the menu structure, losing track of what state the screen was in. GUIs helped mitigate that problem by minimizing the number of state changes that the screen went through. In modern GUIs, a great deal of work is put into keeping common features on the screen at all times and making sure that the user does not get confused by hidden states. It is ironic, then, that the code that implements these "stateless" GUIs is strongly state driven. In such GUIs, the code must figure out which menu items and buttons to gray out, which subwindows should appear, which tab should be activated, where the focus ought to be put, and so on. All these decisions are decisions about the state of the interface. I learned a long time ago that controlling these factors is a nightmare unless you organize them into a single control structure. That control structure is best characterized as an FSM. Since those days, I have been writing almost all my GUIs using FSMs generated by SMC or its predecessors. Consider the state machine in Listing 36-15. This machine controls the GUI for the login portion of an application. On getting a start event, the machine puts up a login screen. Once the user presses the Enter key, the machine checks the password. If the password is good, the machine goes to the loggedIn state and starts the user process (not shown). If the password is bad, the machine displays a screen so informing the user. The user can try again by clicking the OK button but otherwise clicks the Cancel button. If a bad password is entered three times in a row (thirdBadPassword event), the machine locks the screen until the administrator password is entered. Listing 36-15. login.sm
What we've done here is to capture the high-level policy of the application in a state machine. This high-level policy lives in one place and is easy to maintain. It vastly simplifies the rest of the code in the system, because that code is not mixed with the policy code. Clearly, this approach can be used for interfaces other than GUIs. Indeed, I have used similar approaches for textual and machine/machine interfaces as well. But GUIs tend to be more complex than those others, so the need for them, and the volume of them, is greater. GUI Interaction ControllersImagine that you want to allow your users to draw rectangles on the screen. The gestures they use are as follows. A user clicks the rectangle icon in the pallet window, positions the mouse in the canvas window at one corner of the rectangle, presses the mouse button, and drags the mouse toward the desired second corner. As the user drags, an animated image of the potential rectangle appears on the screen. The user manipulates the rectangle to the desired shape by continuing to hold the mouse button down while dragging the mouse. When the rectangle is right, the user releases the mouse button. The program then stops the animation and draws a fixed rectangle on the screen. Of course, the user can abort this at any time by clicking a different pallet icon. If the user drags the mouse out of the canvas window, the animation disappears. If the mouse returns to the canvas window, the animation reappears. Finally, having finished drawing a rectangle, the user can draw another one simply by clicking and dragging again in the canvas window. There is no need to click the rectangle icon in the pallet. What I have described here is a FSM. The state transition diagram appears in Figure 36-5. The solid circle with the arrow denotes the starting state of the state machine.[4] The solid circle with the open circle around it is the final state of the machine.
Figure 36-5. Rectangle interaction state machineGUI interactions are rife with FSMs. They are driven by the incoming events from the user. Those events cause changes in the state of the interaction. Distributed ProcessingDistributed processing is yet another situation in which the state of the system changes based on incoming events. For example, suppose that you had to transfer a large block of information from one node on a network to another. Suppose also that because network response time is precious, you need to chop up the block and send it as a group of small packets. The state machine depicting this scenario is shown in Figure 36-6. It starts by requesting a transmission session, proceeds by sending each packet and waiting for an acknowledgment, and finishes by terminating the session. Figure 36-6. Sending large block, using many packets
|