Part 3 - Functions
As Uncle Bob defines in his book, Clean Code: A Handbook of Agile Software Craftsmanship, functions are the first tear in any program. All the code we write goes into a function, executing some action inside it, returning a result or not. This is the motive why it's so important to understand how to construct a function
in a good way.
The First Rule of Functions
"Functions should be small"
The objective of a function is to tell a story in an objective way. It should be focused on doing one thing, and doing it well. The best way to
achieve this is by being small.
They are some conventions about the sizing. You can consider that a good function should have between 4~6 lines of code. Of course, it turns out into a big amount of small functions. However, if you remember the Scope
Rule, it's better lots of small self-descriptive functions instead of a big one with lots of responsibilities.
If you have a well-defined function, a well-defined class in a
well-defined namespace, you have no motive to worry about the "Sea of Functions".
A Class?
When you have a function that can be divided into several functional areas, and then you have variables that are being used for all these areas, what you should really have is a class. Remember that classes are
nothing more than a group of functions (methods) with a set of variables
(properties). Be aware of the complexity applied to your functions.
The One Thing Meaning
If a function is composed of many different sections, it's clearly doing more than one thing. The level of abstraction should be
considered.
For example, look at this piece of code:
Consider the rule of Extract Till You Drop, which means
you will continue extracting one function from another until you
can't get it anymore.
Here is an example of a refactored compound of functions, each
one with their intent + correct level of abstraction:
Perceive that the naming rule that we talked about in the previous post is applied here. Lots of os small well-descriptive functions. The hierarchy is top-bottom and you can understand what is happening during your lecture.
Switch Statements
By nature, switch statements are mean to do more than one thing (against SRP).
Sometimes there is no way and you should apply some decision structure like this in your code. The idea in this scenario is to add the switch statement in a low-level
class and use externally through polymorphism.
Look at this method:
To attend OCP and SRC in a good way, we can use the abstract
factory:
Now, the runtime dependency is still the same. However, the source-dependency is inverted, providing us a way to deploy independently the changes, without affecting all the dependent methods (that are pointing to the interface). Exposing an interface that can be used as the public contract made it possible for new consumers of this functionality be added, without affecting the ones that are implemented already.
Arguments
Regarding arguments, we have some conventions that can help us:
- The number of arguments: in a general way, we can say that the maximum of three arguments is acceptable (the same rule applied for constructors). Instead of passing parameters or objects to functions/constructors, you can use setter functions. Don't forget that, when you pass many arguments, you can incur the problem of sending parameters with different levels of abstraction.
- Boolean arguments: booleans represent different flows by themselves. Instead of sending a boolean as a parameter, write to specific functions for the true and false paths.
- Output arguments: this type of argument is difficult to understand because when we call a function it's normal that we think about sending a parameter and returning a value. However, returning a value from an external variable is not an easy way to approach a problem, turning the code less readable.
- Null arguments: passing null arguments are considered worst than passing a boolean as an argument. Use the same approach for boolean arguments, create two functions, one to handle the non-null values, another to handle the null ones. Avoid the use of a null argument as a pseudo-boolean. Uncle Bob doesn’t believe in defensive coding, adding lots of null checks into a function because. You should believe in your tests. If you don’t trust them, you will go for defensive programming as much as possible. If you are always checking for nulls, that means your tests don't prevent you from passing those nulls.
Command/Query Separation
The concept of command/query separation tells that a
function should change the state of an object, or return some information of an object, not do both. For
example:
I hope you have enjoyed this step. Bellow the list of the other
chapters:
- Part 1: The Art of Clean Code
- Part 2: Meaningful Names
- Part 3: Functions
---------------------------------------------------------------------------------------------------------------------
---------------------------------------------------------------------------------------------------------------------
Help us to maintain the MyLifeInDev running:
No comments:
Post a Comment