From SelfDocumentingCode
When calculating complex formulae, it is useful to break up the calculation into intermediate temporary variables.
Rationale:
Mathematical formulae of any substantive form are generally complex and difficult to understand, especially given the limited syntactic forms provided by the language (e.g. how do you draw an integral sign? a square root?).
Moreover, it is likely to introduce bugs when writing these formulae, and unlikely to find them easily.
Instead, break these fomulae into subformulae whose values are assigned to intermediate variables. Not only will it be easier to verify the smaller, less complex formulae, but it will be easier to read. Plus, as a bonus, you get to assign names to each subformula, a variation on the theme of NamedConstants.
It can also be useful to apply this concept recursively for the subformulae.
Intermediate values can also be read whilst you are in the debugger. If the expression is all on one line, you can't step through it.
Arguments:
"All those variables eat memory."
They won't eat that much memory. Small trade off for easier maintenance (CodeForTheMaintainer!). And modern compilers will optimize this anyway. Even if they don't, they have to store the variable anyway for the duration of the calculation. Besides, you can always scope them with braces (see BracesAreGood) to ensure that they are destroyed when they aren't needed.
Also, "automatic" (stack-based) variables reuse memory across functions, so the "eating memory" argument is very weak.
In most compilers, the intermediate values will not use any memory space. The compiler will keep the variable in the working register even if you declare a storage location.
(Optimize for memory usage only after actual measurements of the running code show that you have a problem.)
"All those variables need names and thus pollute the namespace."
Once again, you can scope the names using braces if this is a real problem, although it is uncommon to have conflicting names. Try to give meaningful names to the variables, not just "temp" or "foo". It's almost self-defeating to do otherwise.
"I can optimize the formula by, say, canceling factors. If I use intermediate values, this is lost."
Well, the speed hit shouldn't be too bad. Besides, there's no reason you can't simplify the formula first, then break out the subformulae of the simplified formula.
"I should break each subformula into a named function that describes what is being calculated, rather than a variable in a complex function.
... err anyone? ...
This reminds me of a couple of MartinFowler's refactorings: ReplaceTempWithQuery and InlineTemp?. Sometimes it's nice to have a query method that you can call to get intermediate results (because of decoupling and possible LazyEvaluation) and other times you want the temp (for speed and simplicity). -- PhilGoodwin
[This pattern is like MartinFowler's ReplaceQueryWithTemp.]
Exceptions:
If the formula is easy to follow, don't bother.
Template metaprogramming likely will not benefit from this either, but template metaprogramming is very difficult to understand anyway.
Examples:
Compare
void CHedron::Paint() { // Paint this hedron after rotating it and projecting its // world coordinates onto the screen. PaintAt( Transform( Rotate( this, m_dYaw, m_dPitch, m_dRoll ).GetOrigin(), g_WorldToScreenTransform ) ); }with
void CHedron::Paint() { CHedron Rotated = Rotate( this, m_dYaw, m_dPitch, m_dRoll ); CPoint ptScreen = Transform( Rotated.GetOrigin(), g_WorldToScreenTransform ); PaintAt( ptScreen ); }
See also: ThickBreadSmell