The Psychology of Quality and More
CHAPTER 8 : Language Usage
8.3 Using 'if'
The humble if is the cornerstone of computing, allowing decisions to be made and alternative code paths to be taken (coupled with goto it could even be used to dispense with all other loop and control statements!).
8.3.1 Negative comparisons
A negated comparison is generally more difficult to understand than a positive one. A double negation is even more difficult:
if ( !DatabaseUninitialized )
This is effectively a double negative, saying, "if the database is not not initialized." It is clearer to choose variables and functions where they result in positive comparisons:
if ( DatabaseInitialized )
8.3.2 Nested 'if' and order of evaluation
Where a complex comparison occurs, it can be broken down into a series of if statements:
if ( BrakeStatus == OK )
This breaks each decision into a clear chunk in 'one action per line', with each successive if being an effective logical 'and'. It also explicitly describes the order and conditions for evaluation.
However, it decreases complexity per line only at the alternate complexity cost of increasing both the level of nesting and the number of lines. A single statement is usually better:
if ( (BrakeStatus == OK) && (Speed < SP_BRAKE_MAX) )
Confusion can be caused by use of the implicit order of evaluation and conditional evaluation:
/* FlagError only called if FlushBuffer fails */
In this type of situation, it is more explicit to use separate lines to show the alternative action:
if ( FlushBuffer() != FB_FAIL )
The if..else construct is used in a binary decision where there are two clear alternative actions. Usually, one of these actions will be major and the other minor. It helps the reader understand the purpose of the code more easily if the major action is put after the if:
if ( LiquidLevel < DangerLevel )
Sometimes, the use of if..else is questionable:
if ( BrakeStatus == FAILED )
Is it being explicit to use the else here? Not really, as the subsequent code probably does not care that the brakes have not failed. Also, a level of nesting can be saved by leaving the else out (more, if there are multiple such test/returns).
The else..if construct effectively forms a multiway branch, with the final else giving the default branch. This gives an alternative to the switch statement when the comparisons are of more than simple integer values:
if ( EntrantAge <= MAX_JUNIOR_AGE )
Using the final else statement to process unexpected items is being consistent with the normal style of a switch default.
And the big