This is not to say that the Visual Basic calculator does not attempt to handle the context. Quite the contrary, if you look at the calculator code (available from the companion web site at www.state-machine.com/psicc2/, in the directory <qp>\resources\vb\calc.frm), you'll notice that managing the context is in fact the main concern of this application. The code is littered with a multitude of global variables and flags that serve only one purpose: handling the context. For example, DecimalFlag indicates that a decimal point has been entered, OpFlag represents a pending operation, LastInput indicates the type of the last button press event, NumOps denotes the number of operands, and so on. With this representation, the context of the computation is represented ambiguously, so it is difficult to tell precisely in which mode the application is at any given time. Actually, the application has no notion of any single mode of operation but rather a bunch of tightly coupled and overlapping conditions of operation determined by values of the global variables and flags.
Listing 2.1 shows the conditional logic in which the event-handler procedure for the operator events (+, – , *, and /) attempts to determine whether the – (minus) button-click should be treated as negation or subtraction.
Listing 1:
Private Sub Operator_Click(Index As Integer)
. . .
Select Case NumOps
Case 0
If Operator(Index).Caption="-" And LastInput <> "NEG" Then
ReadOut = "-" & ReadOut
LastInput = "NEG"
End If
Case 1
Op1 = ReadOut
If Operator(Index) .Caption="-" And LastInput <> "NUMS" And
OpFlag <>"-" Then
ReadOut = "-"
LastInput = "NEG"
End If
. . .
View image of author's code
The approach exemplified in Listing 2.1 is a fertile ground for the "corner case" behavior (a.k.a. bugs) for at least three reasons:
• It always leads to convoluted conditional logic (a.k.a. "spaghetti" code).
• Each branching point requires evaluation of a complex expression.
• Switching between different modes requires modifying many variables, which can easily lead to inconsistencies.
Convoluted conditional expressions like the one shown in Listing 2.1, scattered throughout the code, are unnecessarily complex and expensive to evaluate at runtime. They are also notoriously difficult to get right, even by experienced programmers, as the bugs still lurking in the Visual Basic calculator attest. This approach is insidious because it appears to work fine initially, but doesn't scale up as the problem grows in complexity. Apparently, the calculator application (overall only seven event handlers and some 140 lines of Visual Basic code including comments) is just complex enough to be difficult to get right with this approach.