Functions are the first line of an organization in any program, writing them well is very important. Uncle Bob Martin has arranged his thoughts into simple sections in each chapter in the book clean code. In summary, to write clean beautiful functions, here are the rules to follow:
Rules of writing beautiful functions
- Functions should be small
- Functions should be smaller than the what you got from following rule 1
- Blocks within if, else, while statements should be one line long (that line should be a function call)
- A function should always do just one thing and they should do it well: If a function does only those steps that are one level below the stated name of the function, then it's doing one thing.
- A way to know if 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. In those cases extract that other function.
- Statements within a function should have only one level of abstraction: Mixing levels of abstraction only creates confusion
- Step Down Rule: When you write a program, every function should be followed by those at the next level of abstractions so anyone can read and follow through the program and understand the flow.
- In case of switch statement, wherever possible, use Abstract factory and bury the switch statement deep inside this abstract factory
- Use descriptive names for functions: give descriptive names to public and private methods as well, good names go a long way. Longer names are better if they explain better
- Use a naming convention that allows multiple words to be easily read in the function name: Don't be afraid to take your time in choosing good names
- Be consistent with function naming in a module. Use same phrases, nouns, and verbs throughout
- An ideal number of arguments for a function is zero (niladic). Next, comes one (monadic), followed closely by two (dyadic). Three or more arguments (triadic) should be avoided where possible: Arguments always take conceptual power as well as are harder from a testing point of view
- Passing a boolean into a function as argument is a truly terrible practice: It says that the function does one thing when the boolean is true, and other when its false: Split the function into two functions with those two cases
- Dyadic functions (Two arguments): They come at a cost. You need to remember the order. For appending to a file, for example, take the function and make it a member of a class File and call it through the class instance passing just one argument
- If a function seems to need more than two or three arguments, it is likely that some of the arguments ought to be wrapped in a class of their own or should use a hash(dictionary or you can say an object) instead.
- Use verbs and keywords to make function names more explainable and explain what it is doing: for e.g. instead of write(name) which is using a verb you can add keyword to explain it better writeField(name)
- In case of temporal coupling: Indicate that coupling and side effect in the name
- Iin case of passing an output argument to a function such as a file to append, separate it using OO principles and call it from within a file instance of a class File
- Separate command from query: Your function should either change the state of an object or it should return some information about the object, doing both leads to confusion: for e.g. public boolean set(String attribute, String value), the function sets the value of a named attribute and returns true is it is successful and false if no such attribute exists. if we call the function using if (set(*username, *unclebob)), it is unclear if it's setting it successfully or checking an old value. The way to resolve it is to separate the command from the query like check for attributeExists(*username) and then call set function.
- Always prefer exceptions over returning error codes: use try-catch or begin rescue to handle any error, error codes adds external dependencies.
- Extract bodies of begin/rescue into functions of their own
- Try/catch or begin/recuse should be the only blocks in a function, don't add any more code after that
- Don't repeat yourself: Use functions and OO objects to avoid repetitions at any cost. Duplication is the root of all evil in software
Following these rules, steps to write clean functions are
- Start with the long complicated easy flow and solve the problem and make it work first, it may have lots of indenting and nested loops, have long argument lists, with arbitrary names and have duplicated codes.
- Write the tests as well.
- After that go ahead and refine, split, change names and eliminate duplication using the rules above.
- If needed extract out a whole class.