15.1. if Statements

 < Free Open Study > 

15.1. if Statements

Depending on the language you're using, you might be able to use any of several kinds of if statements. The simplest is the plain if or if-then statement. The if-then-else is a little more complex, and chains of if-then-else-if are the most complex.

Plain if-then Statements

Follow these guidelines when writing if statements:

Write the nominal path through the code first; then write the unusual cases Write your code so that the normal path through the code is clear. Make sure that the rare cases don't obscure the normal path of execution. This is important for both readability and performance.

Make sure that you branch correctly on equality Using > instead of >= or < instead of <= is analogous to making an off-by-one error in accessing an array or computing a loop index. In a loop, think through the endpoints to avoid an off-by-one error. In a conditional statement, think through the equals case to avoid one.

Put the normal case after the if rather than after the else Put the case you normally expect to process first. This is in line with the general principle of putting code that results from a decision as close as possible to the decision. Here's a code example that does a lot of error processing, haphazardly checking for errors along the way:

Cross-Reference

For other ways to handle error-processing code, see "Summary of Techniques for Reducing Deep Nesting" in Section 19.4.


Visual Basic Example of Code That Processes a Lot of Errors Haphazardly
 OpenFile( inputFile, status ) If ( status = Status_Error ) Then    errorType = FileOpenError       <-- 1 Else    ReadFile( inputFile, fileData, status )       <-- 2    If ( status = Status_Success ) Then       SummarizeFileData( fileData, summaryData, status )       <-- 3       If ( status = Status_Error ) Then          errorType = ErrorType_DataSummaryError       <-- 4       Else          PrintSummary( summaryData )       <-- 5          SaveSummaryData( summaryData, status )          If ( status = Status_Error ) Then             errorType = ErrorType_SummarySaveError       <-- 6          Else             UpdateAllAccounts()       <-- 7             EraseUndoFile()             errorType = ErrorType_None          End If       End If    Else       errorType = ErrorType_FileReadError    End If End If

(1)Error case.

(2)Nominal case.

(3)Nominal case.

(4)Error case.

(5)Nominal case.

(6)Error case

(7)Nominal case.

This code is hard to follow because the nominal cases and the error cases are all mixed together. It's hard to find the path that is normally taken through the code. In addition, because the error conditions are sometimes processed in the if clause rather than the else clause, it's hard to figure out which if test the normal case goes with. In the following rewritten code, the normal path is consistently coded first and all the error cases are coded last. This makes it easier to find and read the nominal case.

Visual Basic Example of Code That Processes a Lot of Errors Systematically
OpenFile( inputFile, status ) If ( status = Status_Success ) Then    ReadFile( inputFile, fileData, status )       <-- 1    If ( status = Status_Success ) Then       SummarizeFileData( fileData, summaryData, status )       <-- 2       If ( status = Status_Success ) Then          PrintSummary( summaryData )       <-- 3          SaveSummaryData( summaryData, status )          If ( status = Status_Success ) Then             UpdateAllAccounts()       <-- 4             EraseUndoFile()             errorType = ErrorType_None          Else             errorType = ErrorType_SummarySaveError       <-- 5          End If       Else          errorType = ErrorType_DataSummaryError       <-- 6       End If    Else       errorType = ErrorType_FileReadError       <-- 7    End If Else    errorType = ErrorType_FileOpenError       <-- 8 End If

(1)Nominal case.

(2)Nominal case.

(3)Nominal case.

(4)Nominal case.

(5)Error case.

(6)Error case.

(7)Error case.

(8)Error case.

In the revised example, you can read the main flow of the if tests to find the normal case. The revision puts the focus on reading the main flow rather than on wading through the exceptional cases, so the code is easier to read overall. The stack of error conditions at the bottom of the nest is a sign of well-written error-processing code.

This example illustrates one systematic approach to handling normal cases and error cases. A variety of other solutions to this problem are discussed throughout this book, including using guard clauses, converting to polymorphic dispatch, and extracting the inner part of the test into a separate routine. For a complete list of available approaches, see "Summary of Techniques for Reducing Deep Nesting" in Section 19.4.

Follow the if clause with a meaningful statement Sometimes you see code like the next example, in which the if clause is null:

Java Example of a Null if Clause

if ( SomeTest )    ; else {    // do something    ... }


Most experienced programmers would avoid code like this if only to avoid the work of coding the extra null line and the else line. It looks silly and is easily improved by negating the predicate in the if statement, moving the code from the else clause to the if clause, and eliminating the else clause. Here's how the code would look after those changes:

Cross-Reference

One key to constructing an effective if statement is writing the right boolean expression to control it. For details on using boolean expressions effectively, see Section 19.1, "Boolean Expressions."


Java Example of a Converted Null if Clause
if ( ! someTest ) {    // do something    ... }

Consider the else clause If you think you need a plain if statement, consider whether you don't actually need an if-then-else statement. A classic General Motors analysis found that 50 to 80 percent of if statements should have had an else clause (Elshoff 1976).

One option is to code the else clause with a null statement if necessary to show that the else case has been considered. Coding null elses just to show that that case has been considered might be overkill, but at the very least, take the else case into account. When you have an if test without an else, unless the reason is obvious, use comments to explain why the else clause isn't necessary, like so:

Java Example of a Helpful, Commented else Clause
// if color is valid if ( COLOR_MIN <= color && color <= COLOR_MAX ) {    // do something    ... } else {    // else color is invalid    // screen not written to --- safely ignore command }

Test the else clause for correctness When testing your code, you might think that the main clause, the if, is all that needs to be tested. If it's possible to test the else clause, however, be sure to do that.

Check for reversal of the if and else clauses A common mistake in programming if-thens is to flip-flop the code that's supposed to follow the if clause and the code that's supposed to follow the else clause or to get the logic of the if test backward. Check your code for this common error.

Chains of if-then-else Statements

In languages that don't support case statements or that support them only partially you'll often find yourself writing chains of if-then-else tests. For example, the code to categorize a character might use a chain like this one:

C++ Example of Using an if-then-else Chain to Categorize a Character
if ( inputCharacter < SPACE ) {    characterType = CharacterType_ControlCharacter; } else if (    inputCharacter == ' ' ||    inputCharacter == ',' ||    inputCharacter == '.' ||    inputCharacter == '!' ||    inputCharacter == '(' ||    inputCharacter == ')' ||    inputCharacter == ':' ||    inputCharacter == ';' ||    inputCharacter == '?' ||    inputCharacter == '-'    ) {    characterType = CharacterType_Punctuation; } else if ( '0' <= inputCharacter && inputCharacter <= '9' ) {    characterType = CharacterType_Digit; } else if (    ( 'a' <= inputCharacter && inputCharacter <= 'z' ) ||    ( 'A' <= inputCharacter && inputCharacter <= 'Z' )    ) {    characterType = CharacterType_Letter; }

Cross-Reference

For more details on simplifying complicated expressions, see Section 19.1, "Boolean Expressions."


Consider these guidelines when writing such if-then-else chains:

Simplify complicated tests with boolean function calls One reason the code in the previous example is hard to read is that the tests that categorize the character are complicated. To improve readability, you can replace them with calls to boolean functions. Here's how the example's code looks when the tests are replaced with boolean functions:

C++ Example of an if-then-else Chain That Uses Boolean Function Calls
if ( IsControl( inputCharacter ) ) {    characterType = CharacterType_ControlCharacter; } else if ( IsPunctuation( inputCharacter ) ) {    characterType = CharacterType_Punctuation; } else if ( IsDigit( inputCharacter ) ) {    characterType = CharacterType_Digit; } else if ( IsLetter( inputCharacter ) ) {    characterType = CharacterType_Letter; }

Put the most common cases first By putting the most common cases first, you minimize the amount of exception-case handling code someone has to read to find the usual cases. You improve efficiency because you minimize the number of tests the code does to find the most common cases. In the example just shown, letters would be more common than punctuation but the test for punctuation is made first. Here's the code revised so that it tests for letters first:

C++ Example of Testing the Most Common Case First
 if ( IsLetter( inputCharacter ) ) {       <-- 1    characterType = CharacterType_Letter; } else if ( IsPunctuation( inputCharacter ) ) {    characterType = CharacterType_Punctuation; } else if ( IsDigit( inputCharacter ) ) {    characterType = CharacterType_Digit; } else if ( IsControl( inputCharacter ) ) {       <-- 2    characterType = CharacterType_ControlCharacter; }

(1)This test, the most common, is now done first.

(2)This test, the least common, is now done last.

Make sure that all cases are covered Code a final else clause with an error message or assertion to catch cases you didn't plan for. This error message is intended for you rather than for the user, so word it appropriately. Here's how you can modify the character-classification example to perform an "other cases" test:

C++ Example of Using the Default Case to Trap Errors
if ( IsLetter( inputCharacter ) ) {    characterType = CharacterType_Letter; } else if ( IsPunctuation( inputCharacter ) ) {    characterType = CharacterType_Punctuation; } else if ( IsDigit( inputCharacter ) ) {    characterType = CharacterType_Digit; } else if ( IsControl( inputCharacter ) ) {    characterType = CharacterType_ControlCharacter; } else {    DisplayInternalError( "Unexpected type of character detected." ); }

Cross-Reference

This is also a good example of how you can use a chain of if-then-else tests instead of deeply nested code. For details on this technique, see Section 19.4, "Taming Dangerously Deep Nesting."


Replace if-then-else chains with other constructs if your language supports them A few languages Microsoft Visual Basic and Ada, for example provide case statements that support use of strings, enums, and logical functions. Use them they are easier to code and easier to read than if-then-else chains. Code for classifying character types by using a case statement in Visual Basic would be written like this:

Visual Basic Example of Using a case Statement Instead of an if-then-else Chain
Select Case inputCharacter    Case "a" To "z"       characterType = CharacterType_Letter    Case " ", ",", ".", "!", "(", ")", ":", ";", "?", "-"       characterType = CharacterType_Punctuation    Case "0" To "9"       characterType = CharacterType_Digit    Case FIRST_CONTROL_CHARACTER To LAST_CONTROL_CHARACTER       characterType = CharacterType_Control    Case Else       DisplayInternalError( "Unexpected type of character detected." ) End Select

 < Free Open Study > 


Code Complete
Code Complete: A Practical Handbook of Software Construction, Second Edition
ISBN: 0735619670
EAN: 2147483647
Year: 2003
Pages: 334

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net