Chapter 3: Functions [Slide]
- Line counts should not be longer than a screen size
- Line width smaller than
150
characters - Line counts smaller than
100
lines
- blocks within conditional (if/when/while...) statements should be one line long
- 1 or 2 indentation level at most
FUNCTIONS SHOULD DO ONE THING. THEY SHOULD DO IT WELL. THEY SHOULD DO IT ONLY.
- another way to know that a function is doing more than one thing is if you can extract another function from it with a name that is not merely a restatement of its implementation
Functions that do one thing cannot be reasonably divided into sections.
Mixing level's of abstraction make's function confusing
We want the code to read like a top-down narrative. We want every function to be followed by those at the next level of abstraction so that we can read the program, descending one level of abstraction at a time as we read down the list of functions
they can be tolerated if they appear only once, are used to create polymorphic objects, and are hidden behind an inheritance relationship so that the rest of the system can’t see them.
- Don’t be afraid to make a name long. A long descriptive name is better than a short enigmatic name
- A long descriptive name is better than a long descriptive comment
- Don't be afraid to change names
- The ideal number of arguments for a function is zero (niadic).
- Next comes one (monadic), followed closely by two (dyadic).
- Three arguments (triadic) should be avoided where possible.
- More than three (polyadic) requires very special justification—and then shouldn’t be used anyway.
- condition arg to decide what to do
- argument to transform into something else
- event
Using an output argument instead of a return value for a transformation is confusing
should be avoided, it is loudly proclaiming that this function does more than one thing
you can simply convert these functions to monadic by moving it to right class as a member function
same applies to this
using objects for group of variables that declare a concept is better than passing every single one of variables
eg. varargs
functions with these arguments are simply monadic, dyadic and traids
so same rules applies to them
functions and arguments should form a very nice verb/noun pairs eg. write(name)
we can use keywords as well to make them more clear eg.
firstOrNull(predicate)
assertExpectedEqualsActual(expected, actual)
Temporal coupling is coupling that occurs when there are two or more members of a class that need to be invoked in a particular order. eg:
var calculator = new PriceCalculator();
calculator.UpdateCurrencyRates(eur: 1.02, gbp: 1.25);
decimal price = calculator.CalculatePrice(myShoppingCart);
Here, currency rates required to calculate the price of a shopping cart.
should be avoided
public boolean set(String attribute, String value);
if (set("username", "unclebob"))…
if (attributeExists("username")) {
setAttribute("username", "unclebob");
...
}
returning error code* will result in nested conditional checks
extract each block to another function
a function that handles errors should do nothing else
having one enum class for all error codes will force compiler to rebuild every time there is a change in this enum, because all other parts of code have imported this
Duplication may be the root of all evil in software
every function, and every block within a function, should have one entry and one exit.
if you keep your functions small, then the occasional multiple return, break, or continue statement does no harm and can sometimes even be more expressive than the single- entry, single-exit rule.
you cannot reach clean code at the first try! when you start writing code, just jot down your thoughts into any clumsy code that pop's up into your mind, duplicate code, complicated loops, it's all O.K then you start iterating through your code and refine them to smaller functions and clean design while keeping unit tests passing.