Just as architects design buildings by employing the collective wisdom of their profession, so should programmers design apps. Our field is younger than architecture, and our collective wisdom is sparser but growing quickly. We’ve learned that structured programming produces apps that are easier than unstructured apps to understand, test, debug, modify and even prove correct in a mathematical sense.
Figure 6.21 uses UML activity diagrams to summarize C#’s control statements. The initial and final states indicate the single entry point and the single exit point of each control statement. Arbitrarily connecting individual symbols in an activity diagram can lead to unstructured apps. Therefore, a limited set of control statements can be combined in only two simple ways to build structured apps.
For simplicity, only single-entry/single-exit control statements are used—there’s only one way to enter and only one way to exit each control statement. Connecting control statements in sequence to form structured apps is simple. The final state of one control statement is connected to the initial state of the next—that is, the control statements are placed one after another in an app in sequence. We call this control-statement stacking. The rules for forming structured apps also allow for control statements to be nested.
Figure 6.22 shows the rules for forming structured apps. The rules assume that action states may be used to indicate any action. The rules also assume that we begin with the simplest activity diagram (Fig. 6.23) consisting of only an initial state, an action state, a final state and transition arrows.
Rules for forming structured apps |
---|
|
Applying the rules in Fig. 6.22 always results in a properly structured activity diagram with a neat, building-block appearance. For example, repeatedly applying Rule 2 to the simplest activity diagram results in an activity diagram containing many action states in sequence (Fig. 6.24). Rule 2 generates a stack of control statements, so let us call Rule 2 the stacking rule. [Note: The vertical dashed lines in Fig. 6.24 are not part of the UML— we use them to separate the four activity diagrams that demonstrate the application of Rule 2 of Fig. 6.22.]
Rule 3 is called the nesting rule. Repeatedly applying Rule 3 to the simplest activity diagram results in an activity diagram with neatly nested control statements. For example, in Fig. 6.25, the action state in the simplest activity diagram is replaced with a double-selection (if
…else
) statement. Then Rule 3 is applied again to the action states in the double-selection statement, replacing each of these action states with a double-selection statement. The dashed action-state symbols around each of the double-selection statements represent the action state that was replaced. The dashed arrows and dashed action-state symbols shown in Fig. 6.25 are not part of the UML—they’re used here to illustrate that any action state can be replaced with any control statement.
Rule 4 generates larger, more involved and more deeply nested statements. The diagrams that emerge from applying the rules in Fig. 6.22 constitute the set of all possible structured activity diagrams and hence the set of all possible structured apps. The beauty
of the structured approach is that we use only eight simple single-entry/single-exit control statements (counting the foreach
statement, which we introduce in Section 8.4.5) and assemble them in only two simple ways.
If the rules in Fig. 6.22 are followed, an “unstructured” activity diagram (like the one in Fig. 6.26) cannot be created. If you’re uncertain about whether a particular diagram is structured, apply the rules of Fig. 6.22 in reverse to reduce the diagram to the simplest activity diagram. If you can reduce it, the original diagram is structured; otherwise, it’s not.
Structured programming promotes simplicity. Research has shown that only three forms of control are needed to implement any algorithm:
sequence
selection
iteration
Sequence is trivial. Simply list the statements of the sequence in the order in which they should execute. Selection is implemented in one of three ways:
if
statement (single selection)
if
…else
statement (double selection)
switch
statement (multiple selection)
In fact, it’s straightforward to prove that the simple if
statement is sufficient to provide any form of selection—everything that can be done with the if
…else
statement and the switch
statement can be done by combining if
statements (although perhaps not as clearly and conveniently).
Iteration is implemented in one of four ways:
while
statement
do
…while
for
statement
foreach
statement
It’s straightforward to prove that while
is sufficient to provide any form of iteration. Everything that can be done with do
…while
, for
and foreach
can be done with while
(although perhaps not as clearly and conveniently).
Combining these results illustrates that any form of control ever needed in a C# app can be expressed in terms of
sequence structure
if
statement (selection)
while
statement (iteration)
and these can be combined in only two ways—stacking and nesting.