Software Design/Extract repeated expression into a local variable
Checklist question:
- A repeated expression or a result of a function call can be extracted into a local variable?
Why
[edit | edit source]Extracting a repeated expression into a local variable corresponds to the DRY principle. It avoids self-repetition, and thus reduces the change amplification if the extracted expression needs to be altered in the future. It also leaves no chance for introducing a bug by forgetting to alter some occurrence of the expression in the function.
The extracted variable can be given a name that describes the semantics of the expression. The variable helps to abstract its semantics from the details of how the variable's value is obtained. This also withdraws cognitive load from the readers because they don't need to figure out the meaning of the expression every time they visit the code.[1] See practice Extract expression into an explaining variable for further information.
The code may become more efficient because the expression is not computed multiple times. Even when an optimizing compiler is used, sometimes it couldn't perform a CSE optimization itself because it may not be able to prove that the extracted expression's inputs haven't changed between the occurrences, or that the expression (for example, a function call) doesn't have side effects.
In non-blocking concurrent code, extracting field values into local variables might not just improve the performance but be necessary for the correctness. Reading the state (that may be updated concurrently) multiple times may be prone to race conditions.
If there is some static analysis check that emits a false-positive warning on the expression and therefore needs to be suppressed, extracting the expression into a variable allows to reduce the scope of the suppression to the variable declaration from the whole function's body of a block, or to avoid repeating suppressions at every place where the expression appears in the function.
Why not
[edit | edit source]The proliferation of local variables makes it harder to perform refactoring.[2] Local variables might also create a bias against breaking up a large function into smaller ones because developers may feel pity to recompute the expression in several different places and thus lose the performance benefit of caching the computation in a local variable.
If the repeated expression is a function call (especially without arguments) or a read of a field, the argument made above that a repetition of expression makes the code less robust and increases change amplification when the expression has to be changed almost doesn't make sense. On the other hand, cached computation might become a source of bugs itself when the logic of the function (or the logic of other functions called from it, transitively) is changed so that the state used to compute the expression is updated between the occurrences of the expression in the function. For example, when the expression is a read of a field and a write to this field is added between uses of the field in the function, the uses that occur after the write will use the wrong value if the field's value is cached in a local variable.
When an expression is repeated not just within a single function but also across several functions, removing the duplication within each function is a half measure. The expression should be extracted as a function first. Caching calls to a newly extracted function within each function can be considered next.
In a function that already has a substantial number of local variables, extracting a simple repeated expression such as an integer arithmetic operation or a read of a field might not make the function faster, or even make it slower because of register pressure which leads to spilling of local variables to memory in compiled machine code.
Related practices
[edit | edit source]References
[edit | edit source]- ↑ Boswell, Dustin; Foucher, Trevor (2011). The Art of Readable Code. ISBN 978-0596802295. Chapter 8, section "Summary Variables"
- ↑ Refactoring: Improving the Design of Existing Code (2 ed.). 2018. ISBN 978-0134757599. https://martinfowler.com/books/refactoring.html. Chapter 1