< Free Open Study > |
Most layout issues have to do with laying out blocks, the groups of statements below control statements. A block is enclosed between braces or keywords: { and } in C++ and Java, if-then-endif in Visual Basic, and other similar structures in other languages. For simplicity, much of this discussion uses begin and end generically, assuming that you can figure out how the discussion applies to braces in C++ and Java or other blocking mechanisms in other languages. The following sections describe four general styles of layout:
Pure BlocksMuch of the layout controversy stems from the inherent awkwardness of the more popular programming languages. A well-designed language has clear block structures that lend themselves to a natural indentation style. In Visual Basic, for example, each control construct has its own terminator and you can't use a control construct without using the terminator. Code is blocked naturally. Some examples in Visual Basic are shown in Listing 31-6, Listing 31-7, and Listing 31-8: Listing 31-6. Visual Basic example of a pure if blockIf pixelColor = Color_Red Then statement1 statement2 ... End If Listing 31-7. Visual Basic example of a pure while blockWhile pixelColor = Color_Red statement1 statement2 ... Wend Listing 31-8. Visual Basic example of a pure case blockSelect Case pixelColor Case Color_Red statement1 statement2 ... Case Color_Green statement1 statement2 ... Case Else statement1 statement2 ... End Select A control construct in Visual Basic always has a beginning statement If-Then, While, and Select-Case in the examples and it always has a corresponding End statement. Indenting the inside of the structure isn't a controversial practice, and the options for aligning the other keywords are somewhat limited. Listing 31-9 is an abstract representation of how this kind of formatting works: Listing 31-9. Abstract example of the pure-block layout styleIn this example, statement A begins the control construct and statement D ends the control construct. The alignment between the two provides solid visual closure. The controversy about formatting control structures arises in part from the fact that some languages don't require block structures. You can have an if-then followed by a single statement and not have a formal block. You have to add a begin-end pair or opening and closing braces to create a block rather than getting one automatically with each control construct. Uncoupling begin and end from the control structure as languages like C++ and Java do with { and } leads to questions about where to put the begin and end. Consequently, many indentation problems are problems only because you have to compensate for poorly designed language structures. Various ways to compensate are described in the following sections. Emulating Pure BlocksA good approach in languages that don't have pure blocks is to view the begin and end keywords (or { and } tokens) as extensions of the control construct they're used with. Then it's sensible to try to emulate the Visual Basic formatting in your language. Listing 31-10 is an abstract view of the visual structure you're trying to emulate: Listing 31-10. Abstract example of the pure-block layout styleIn this style, the control structure opens the block in statement A and finishes the block in statement D. This implies that the begin should be at the end of statement A and the end should be statement D. In the abstract, to emulate pure blocks, you'd have to do something like Listing 31-11: Listing 31-11. Abstract example of emulating the pure-block styleSome examples of how the style looks in C++ are shown in Listing 31-12, Listing 31-13, and Listing 31-14: Listing 31-12. C++ example of emulating a pure if blockif ( pixelColor == Color_Red ) { statement1; statement2; ... } Listing 31-13. C++ example of emulating a pure while blockwhile ( pixelColor == Color_Red ) { statement1; statement2; ... } Listing 31-14. C++ example of emulating a pure switch/case blockswitch ( pixelColor ) { case Color_Red: statement1; statement2; ... break; case Color_Green: statement1; statement2; ... break; default: statement1; statement2; ... break; } This style of alignment works pretty well. It looks good, you can apply it consistently, and it's maintainable. It supports the Fundamental Theorem of Formatting in that it helps to show the logical structure of the code. It's a reasonable style choice. This style is standard in Java and common in C++. Using begin-end Pairs (Braces) to Designate Block BoundariesA substitute for a pure-block structure is to view begin-end pairs as block boundaries. (The following discussion uses begin-end to refer generically to begin-end pairs, braces, and other equivalent language structures.) If you take that approach, you view the begin and the end as statements that follow the control construct rather than as fragments that are part of it. Graphically, this is the ideal, just as it was with the pure-block emulation shown again in Listing 31-15: Listing 31-15. Abstract example of the pure-block layout styleBut in this style, to treat the begin and the end as parts of the block structure rather than the control statement, you have to put the begin at the beginning of the block (rather than at the end of the control statement) and the end at the end of the block (rather than terminating the control statement). In the abstract, you'll have to do something like what's done in Listing 31-16: Listing 31-16. Abstract example of using begin and end as block boundariesSome examples of how using begin and end as block boundaries looks in C++ are shown in Listing 31-17, Listing 31-18, and Listing 31-19: Listing 31-17. C++ example of using begin and end as block boundaries in an if blockif ( pixelColor == Color_Red ) { statement1; statement2; ... } Listing 31-18. C++ example of using begin and end as block boundaries in a while blockwhile ( pixelColor == Color_Red ) { statement1; statement2; ... } Listing 31-19. C++ example of using begin and end as block boundaries in a switch/case blockswitch ( pixelColor ) { case Color_Red: statement1; statement2; ... break; case Color_Green: statement1; statement2; ... break; default: statement1; statement2; ... break; } This alignment style works well; it supports the Fundamental Theorem of Formatting (once again, by exposing the code's underlying logical structure). Its only limitation is that it can't be applied literally in switch/case statements in C++ and Java, as shown by Listing 31-19. (The break keyword is a substitute for the closing brace, but there is no equivalent to the opening brace.) Endline LayoutAnother layout strategy is "endline layout," which refers to a large group of layout strategies in which the code is indented to the middle or end of the line. The endline indentation is used to align a block with the keyword that began it, to make a routine's subsequent parameters line up under its first parameter, to line up cases in a case statement, and for other similar purposes. Listing 31-20 is an abstract example: Listing 31-20. Abstract example of the endline layout styleIn this example, statement A begins the control construct and statement D ends it. Statements B, C, and D are aligned under the keyword that began the block in statement A. The uniform indentation of B, C, and D shows that they're grouped together. Listing 31-21 is a less abstract example of code formatted using this strategy: Listing 31-21. Visual Basic example of endline layout of a while blockWhile ( pixelColor = Color_Red ) statement1; statement2; ... Wend In the example, the begin is placed at the end of the line rather than under the corresponding keyword. Some people prefer to put begin under the keyword, but choosing between those two fine points is the least of this style's problems. The endline layout style works acceptably in a few cases. Listing 31-22 is an example in which it works: Listing 31-22. A rare Visual Basic example in which endline layout seems appealing If ( soldCount > 1000 ) Then markdown = 0.10 profit = 0.05 Else <-- 1 markdown = 0.05 End If
In this case, the Then, Else, and End If keywords are aligned and the code following them is also aligned. The visual effect is a clear logical structure. If you look critically at the earlier case-statement example, you can probably predict the unraveling of this style. As the conditional expression becomes more complicated, the style will give useless or misleading clues about the logical structure. Listing 31-23 is an example of how the style breaks down when it's used with a more complicated conditional:
What's the reason for the bizarre formatting of the Else clauses at the end of the example? They're consistently indented under the corresponding keywords, but it's hard to argue that their indentations clarify the logical structure. And if the code were modified so that the length of the first line changed, the endline style would require that the indentation of corresponding statements be changed. This poses a maintenance problem that pure block, pure-block emulation, and using begin-end to designate block boundaries do not. You might think that these examples are contrived just to make a point, but this style has been persistent despite its drawbacks. Numerous textbooks and programming references have recommended this style. The earliest book I saw that recommended this style was published in the mid-1970s, and the most recent was published in 2003. Overall, endline layout is inaccurate, hard to apply consistently, and hard to maintain. You'll see other problems with endline layout throughout the chapter. Which Style Is Best?If you're working in Visual Basic, use pure-block indentation. (The Visual Basic IDE makes it hard not to use this style anyway.) In Java, standard practice is to use pure-block indentation. In C++, you might simply choose the style you like or the one that is preferred by the majority of people on your team. Either pure-block emulation or begin-end block boundaries work equally well. The only study that has compared the two styles found no statistically significant difference between the two as far as understandability is concerned (Hansen and Yim 1987). Neither of the styles is foolproof, and each requires an occasional "reasonable and obvious" compromise. You might prefer one or the other for aesthetic reasons. This book uses pure-block style in its code examples, so you can see many more illustrations of how that style works just by skimming through its examples. Once you've chosen a style, you reap the most benefit from good layout when you apply it consistently. |
< Free Open Study > |