This chapter focussed on the writing and naming of functions.
Functions should be kept small, very small.
Functions should not be more than 150 characters long and they should not be 100 lines long. Functions should rarely be longer than 20 lines.
Functions should be transparently obvious about what they do, and they should tell a story.
Blocks and Indenting
Blocks within if, else and while statements should probably only be one line long and that one line should be a function call.
This is beneficial since it keeps the enclosing function small, and also the function will have a descriptive name therefore adding documentary value.
It also implies that functions should not be large enough to hold nested structures. Therefore the indent level of a function should not be greater than one or two. Therefore making functions easier to read.
Do One Thing
Functions should do one thing. They should do it well. They should do it only.
Functions should have only one level of abstraction, if there are more than one level then it indicates that the function can be broken down further into separate smaller functions.
Sections within Functions
There will often be times when a function has several sections within it.
See Lisiting 4-7 on page 71 of the Clean Code Book.
If this is the case then it is obvious that the function is doing more than one thing and should therefore be broken down into separate functions.
Reading Code from Top to Bottom: The Stepdown Rule
Code should read like a top down narrative meaning that every function is followed by another function at the next level of abstraction.
It should read like a list of paragraphs each with one level of abstraction.
See Listing 3-7 of the Clean Code Book.
Descriptive function names should be able to tell you exactly what the function does, the code does what you expect it to do based off of the name given.
Assigning good names to a function is easy when the function is small and only does one thing and the code is more focussed.
Don’t be afraid to make a name long. Long descriptive names are better than short enigmatic name and long descriptive names are better than long descriptive comments.
The ideal number of arguments for a function is zero.
Arguments are not only difficult to conceptualise by the reader, but also difficult to test sine you have to test every possible combination of arguments. And with an increasing number of arguments the testing becomes exponentially more difficult.
There are two main reasons for needing an argument in a function
- You are asking a question about the argument e.g.
- You are operating on that argument, transforming it into something else and returning it e.g.
Flag arguments e.g. boolean should not be used as arguments for functions because it means that the function is no longer doing one thing.
The function performs differently depending on whether the argument is true or false therefore breaking the do one thing only rule.
For example if a function had the signature
render(true) it should be refactored into
Functions with two arguments are harder to understand than monadic functions.
But sometimes they can’t be avoided and actually makes sense.
For example, a function called
plotPoints(int x, int y) makes sense since cartesian axis always come as a pair and they have a natural ordering.
But most of the time there is no reason to have a dyadic function and alternatives should be considered before using them.
Triadic functions are another level of complexity above dyadic functions and should be considered carefully before being used.
When a function seems to need more than two or three arguments, it is likely that those arguments should be wrapped in their own object
makeCircle(double x, double y, double radius)
This can be abstracted further into:
makeCircle(Point center, double radius)
Verbs and Keywords
Function arguments should form a verb/noun pair e.g.
An improvement on the
assertEquals(expected, actual) function name would be
assertExpectedEqualsActual(expected, actual). This explicitly tells you the ordering of the arguments.
Have No Side Effects
Functions with side effects are functions that say they do one thing, but in addition do something else, other hidden things.
For example it may make changes to class or global variables, or perform some operation that is not stated in the function name.
It can result in temporal couplings and order dependencies.
For example, a function called
checkPassword() would be expected to just check that a password is valid.
However if this function also initialized the session then this session initialization is a side effect that the caller of the function would not be aware of (unless they read the code) and could therefore accidentally call this method and overwrite an existing session.
This side effect causes a temporal coupling since the
checkPassword() can only be called at a certain time (when it is safe to initialize the session).
The session initialization should be removed from the
Extract Try/Catch Blocks
The bodies of try catch functions should be extracted into their own functions.
Error handling should be one thing, so if a function handles errors, it should have the try/catch block and that is is. The function should do nothing else.
Don’t duplicate code
Code duplication not only bloats code but leads to more errors. When some of this duplicated code has to change, all of the other duplications have to be updated as well.
It also negatively impacts the readability of the code.
The Clean Code book can be found on Amazon