It's a popular myth that Visual Basic has changed programming. Not so: programming is as hard as it always was, it's just that most
Visual Basic projects aren't doing it. The typical VB 'client-server' project is a database front-end and Visual Basic is little more than the scaffolding on which to hang the SQL. It's tempting to concentrate on form
layouts and base our estimates on how many forms we need, glossing over seemingly peripheral issues such as navigation and form interactions. Inevitably, the comfortable delusion of VB as traditional 4GL fades as work
progresses. The truth, scribbled in the margin of many a project debriefing report, is that programming in Visual Basic is programming first, and Visual Basic second.
Here we're going to look at a design model, the Finite State Machine, which is particularly useful in the design of event-driven systems. As we'll see, it's also a very useful way of coping with the kind of
'combinatorial explosions' that can arise when we engineer highly interconnected systems such as multi-form user interfaces. In a multi-form GUI we can dramatically increase the complexity of the underlying code by
allowing multiple forms to be active at the same time; this is because manipulating data on one form can affect other forms that are displayed at the same time. Even a modest-sized application can be extremely complex
because we need to examine every possible combination of forms that can be active at the same time and then consider the effects of all possible inputs for each combination. Implementing modes (using Modal forms, for
example) will help to limit form interactions, but doing the analysis ad hoc invites unplanned side effects, and inevitably we'll fail to plan for some situations. Figure 1
shows an application that manages a cascade of forms that leads the user down through successive layers of data. If we use modeless forms, editing the data on form A invalidates the data on forms B and C. We also need to consider what happens if the user closes form A before closing forms B and C. Clearly, we need to decide whether these actions have any useful meaning in the application; if they don't, we can simply prevent them. If they do, we want to know up front because they can affect the implementation (we don't want the forms to share data, for example).
Figure 1: A cascade of modeless forms You might be feeling sceptical about the complexities of a three-form application, but some simple arithmetic can be
illuminating. There are seven possible ways in which combinations of the modeless forms A, B, and C can be active (let's call these combinations 'states'). Now let's say the user presses the Cancel
button on form A. Let's also assume we've decided never to leave forms B and C up when the user closes form A. It's clear that the event handler for form A's Cancel Click can't simply unload the
form – it must look around to see what other forms are up and maybe close those forms too. If we add another form to the application, the number of states
goes up to 15. Even discarding the states that don't contain form A, we're left with 8 different situations to consider in each Cancel Click event handler. In fact, the number of states (combinations of forms) is 2
n-1, where n is the number of forms. This number increases geometrically as we add forms, which means the number of states gets out of hand very quickly (this is the
so-called 'combinatorial explosion'). There is a set of events for which we must consider all of these states, and the handlers for such events need to be aware of the environment and adjust their behaviour accordingly.
It should be clear by now that we need a formal way to define the interactions between forms. The model we'll use to track form interactions is the
Finite State Machine (FSM), which is essentially an abstract representation of a set of states and events. Next... |