Write Short Units of Code
Limit the length of code units to 15 lines of code. Do this by not writing units that are longer than 15 lines of code in the first place, or by splitting long units into multiple smaller units.
Units are the smallest groups of code that can be maintained and executed independently. In Java, units are methods or constructors. A unit is always executed as a whole. It is not possible to invoke just a few lines of a unit. Therefore, the smallest piece of code that can be reused and tested is a unit.
The advantages of short units are that they are easy to test, easy to analyze, and easy to reuse.
Following this guideline is not difficult when you know the right techniques, but it requires discipline. When writing a new unit, never let it grow beyond 15 lines of code. That means that well before you reach 15 lines of code, you need to start thinking about how to add further functionality. Does it really belong in the unit you are writing, or should it go into its own unit? When a unit grows beyond 15 lines of code despite your efforts, you need to shorten it.
The most basic version of the start method checks whether the game is already in progress. If so, it silently returns; otherwise, it updates inProgress, a private member to keep track of its state:
if we add code that tells all observers the level has been lost if the player has died, and that tells all observers that the level is won if any pellets are left:
Adding the code to update observers made our unit grow to 16 lines of code (and 18 lines in total, including the 2 lines that contain comments). After testing the behavior of this new code, you are probably already thinking about the next functionality to implement. However, you need to refactor?
As you can see, the unit (the method called
start) that had grown to 16 lines of code is now back to 7 lines of code, well below the limit of 15 lines. A new unit (method), called
updateObservers, has been added. It has 12 lines of code, which is also under the limit of 15 lines.
The new method still has two responsibilities, as indicated by the comments. We can refactor the code further, applying Extract Method two more times:
There is no need for the comments anymore: they have been replaced by the names of the new methods. Using short units makes source code self-explanatory, as the names of the methods take over the role of comments.
There is a price, however: the total number of code lines has increased, from 16 to 25.
Writing maintainable code is always a trade-off between different guidelines. When splitting a unit into multiple units, you might increase the total number of code lines. However, you have decreased the length and complexity of units that need to be tested and understood. Therefore, maintainability has improved.
Sometimes, you want to extract a method that does access local variables. It is always possible to pass local variables as parameters to the extracted method. However, this may lead to long parameter lists. Return values can be even more troublesome, as in Java you can return only a single value from a method. In these cases, you can use a second refactoring technique, called Replace Method with Method Object.
If you apply the Extract Method technique, you will have to pass seven parameters to the extracted method:
Let us try the Replace Method with Method Object technique instead.
Not only have we ended up only with methods shorter than 15 lines of code and avoided creating methods with long parameter lists, but the code is actually easier to read, test, and reuse.
“Writing short units means having more units, and therefore more method calls. That will never perform.”
Indeed, theoretically, there is a performance penalty for having more units. There will be more method invocations (compared to having fewer, longer units). For each invocation, a bit of work needs to be done by the Java Virtual Machine (JVM). In practice, this is almost never a problem. In the worst case, we are talking about microseconds. Unless a unit is executed hundreds of thousands of times in a loop, the performance penalty of a method invocation is not noticeable. Also, the JVM is very good at optimizing the overhead of method invocations.
“Code becomes harder to read when spread out over multiple units.”
Well, psychology says that is not the case. People have a working memory of about seven items, so someone who is reading a unit that is significantly longer than seven lines of code cannot process all of it. The exception is probably the original author of a piece of source code while he or she is working on it (but not a week later).
“My unit really cannot be split up.”
Sometimes, splitting a method is indeed difficult. Take, for instance, a properly formatted
switchstatement in Java. For each case of the
switchstatement, there is a line for the case itself, at least one line to do anything useful, and a line for the
breakstatement. So, anything beyond four cases becomes very hard to fit into 15 lines of code, and a
casestatement cannot be split.
However, it is true that sometimes a source code statement simply cannot be split. A typical example in enterprise software is SQL query construction.
This example has 16 lines of code. However, there are just three statements. The second statement contains an expression that spans nine lines. Indeed, you cannot extract just this statement; neither the Extract Method nor the Replace Method with Method Object technique is applicable, at least not directly.
If you are faced with a unit that seems impossible to refactor, do not ignore it and move on to another programming task, but indeed raise the issue with your team members and team lead.
When a refactoring seems possible but doesn’t make sense, rethink the architecture of your system.
“Putting code in
doSomethingThreehas no benefit over putting the same code all together in one long
Actually, it does, provided you choose better names than
doSomethingTwo, and so on. Each of the shorter units is, on its own, easier to understand than the long
doSomething. More importantly, you may not even need to consider all the parts, especially since each of the method names, when chosen carefully, serves as documentation indicating what the unit of code is supposed to do. Moreover, the long
doSomethingtypically will combine multiple tasks. That means that you can only reuse
doSomethingif you need the exact same combination. Most likely, you can reuse each of
doSomethingTwo, and so on much more easily.
Put code in short units (at most 15 lines of code) that have carefully chosen names that describe their function.