Wednesday, November 03, 2004

Decision Trees

Almost all languages have the opportunity to program decisions with an if-then-else statement. Generally you have variable, say foo, which equals something, in our case 17, and then you compare the variable to something and if its true one action happens or if its false another action happens. For our examples, we will use SpyCode instead of a real programming language.

foo = 17;
if ( foo equals 18 ) then
    --- Do this ---
else
    --- Do that ---
endif;

Most if statements also accommodate an elseif clause:

foo = 17;
if ( foo equals 18 ) then
    Print "You are old enough to vote in the US."
elseif ( foo equals 17 ) then
    Print "Please register. You can vote next year in the US."
else
    Print "Some irrelevant text."
endif;

As a quality assurance engineer, it turns my stomach to see repeated elseif clauses in an if-then-else statement. It is even worse to see nested if-then-else statements. The code quickly deteriorates into an untestable state.

Fortunately, most languages accommodate a case statement.

switch ( foo ) {
   case "18":
      Print "You are old enough to vote in the US."
      break;
   case "17":
      Print "Please register. You can vote next year in the US."
      break;
   default:
      Print "Some irrelevant text."
}


Whenever possible a switch-case statement should be used instead of an if statement! The execution time is always faster and the code is always more testable.

One of my favorite white board discussions is on Cyclomatic Complexity. A case statement always has a cyclomatic complexity of 1 meaning that there is only one path through the code. An if statement automatically creates two paths through the code.

If the code had a single IF statement there would be two paths through the code, one path where the IF statement is evaluated as TRUE and one path where the IF statement is evaluated as FALSE. (thefreedictionary.com)

Adding elseif conditions to the if-then-else statement rapidly increases the number of potential paths (and the number of test cases) through the code. Remember, when testing an if condition it is not sufficient to create a test case for foo = 18. You must test the boundaries of 18 (17 and 19 if dealing with integers) as well as the unexpected (foo is undefined, foo is not a number, foo is negative) whereas a case statement the testcases are very specific. But testing is a whole subject unto itself . For that matter McCabe's Cyclomatic Complexity is also.

In summary, avoid if-then-else statements and use switch-case statements.