Refactor Default Or Else

last modified: November 18, 2001

Or else what? Hmmf, I don't have to refactor nuthin'.


[CategoryRefactoring/RefactoringLanguage]

You can sometimes simplify conditional logic by "defaulting" to a common result, and then expressing only the deviations from the default in logic:

Private Sub RefactorDefaultOrElse()

From this...

If Condition() Then
    lvar = Value()
Else
    lvar = Default()
End If

to this...

lvar = Default()
If Condition() Then
    lvar = Value()
End If

It's better to set a default value first if the conditional logic to set the variable is complex. This ensures that you don't end up with an unintialized or improperly initialized variable.

It's not obvious to me why the latter form is safer or more intuitive. Perhaps the example is too simple?

It's better to use the "else ... default" form if the default is complex or expensive to compute.

If both conditions apply, use a flag:

bUseDefault = True 
If Condition() Then 
    lvar = Value() 
    bUseDefault = False 
End If 
If bUseDefault Then 
    lvar = Default() 
End If 

(This is way overkill for the simple example given. ;-)

End Sub


Example, pulled from RefactorNegateIf:

Here's an example refactoring, where using the "Refactor Negative IF" rule improved the code:

From...

If ConditionOne() And ConditionTwo() Then
    X = 1
Else
    X = 2
End If

If ConditionOne() And ConditionThree() Then
    Y = 1
Else
    Y = 2
End If

to...

X = 2
Y = 2

If ConditionOne() Then
    If ConditionTwo() Then
        X = 1
    End If
    If ConditionThree() Then
        Y = 1
    End If
End If

(It also helps that "ConditionOne()" is a really strange and unusual condition: The "<var> = 2" lines are the normal business rules, while the "<var> = 1" lines are exceptions/overrides.)

Thus, we end up saying...

-- JeffGrigg

Of course, if this block of code is the only code in the body of a method, you can convert if ConditionOne() to a GuardClause. (if not ConditionOne() then return end if)


On a somewhat related note, I'm quite a fan of the form that you'd more often see in functional programming. The idea it instead of using several branches, each of which having the side-effect of initialising the variable, you have a single assignment which sets the variable to the result of some expression. For example, in pseudo code:

X = if (Condition)
        value1;
    else
        value2;

I find this form quite nice, because it makes it obvious that your variables are all initialised. Unfortunately to factor things like this in C-style languages I suspect you'd often need to make new functions, which might be inconvenient for a lot of simple cases. -- LukeGorrie

Not necessarily. C does have the ?: conditional expression evaluation operator, after all.

That's true, but I still suspect you'd need to go to functions pretty often, good though ?: is for an 'if-else' form. Consider some erlang code which would seemingly need to be function'ified in C: (admittedly its politely asking for function'ification in Erlang too - perhaps I should revise the example if that's a bother)

Threads = case Argument of
              a_few       -> 1;
              a_fair_few  -> 5;
              quite_a_lot -> 10
         end,

As a side-note, another thing I love about that form is that the code is only defined for a few special atoms - i.e., there's no "default", so it's undefined for nonsensical arguments. I hate it when I find a bogus value in my code, only to see that it's hopped half way across my program from its source by slipping through defaults and so forth. Java null references spring to mind. -- LukeGorrie

C/C++:

X = Condition ? value1 : value2;
Threads = a_few ? 1 : a_fair_few ? 5 : quite_a_lot ? 10 : -1;

VisualBasic:

X = IIf(Condition, value1, value2)
Threads = IIf(a_few, 1, IIf(a_fair_few, 5, IIf(quite_a_lot, 10, -1)))

If "Threads" is a Variant, then you could use "Nothing" instead of "-1", or other arbitrary value.

This might be clearer if you use the Choose function. This function has the following syntax:

Choose(condition1, value1, condition2, value2, ..., conditionN, valueN)

Each condition is evaluated, moving left to right, and the first one that evaluates to True causes the corresponding value to be returned. So you could write:

x = Switch(argument = a_few, 1, argument = a_fair_few, 5, argument = quite_a_lot, 10)

The biggest drawback is that there is no short-circuiting - all conditions are evaluated.


X = if (Condition)
        value1;
    else
        value2;

... in a real functional language is:

operation_on_x (condition_foobar Condition
   where 
     condition_foobar true is
       value1
     condition_foobar false is
       value2
  )

... or something like that

-- AluoNowu (nov17-01)


Loading...