Object Calisthenics in Golang
Jeff Bay introduced the term Object Calisthenics in his book Thought Works Anthology. It is a set of good practices and programming rules that can improve the quality of our code.
I saw these techniques for the first time when Rafael Dohms and Guilherme Blanco did a great job adapting them from the Java language to PHP. If you write code in PHP and do not know Object Calisthenics, I recommend two of the presentations made by them:
Your code sucks, let’s fix it Object Calisthenics Applied to PHP
But what are these rules, anyway? They are, in the original version:
- One level of indentation per method.
- Don’t use the ELSE keyword.
- Wrap all primitives and Strings in classes.
- First class collections.
- One dot per line.
- Don’t abbreviate.
- Keep all classes less than 50 lines.
- No classes with more than two instance variables.
- No getters or setters.
As I mentioned earlier, Jeff created these rules based on the Java language and they need adaptations for other environments. Just as Rafael and Guilherme did for PHP, we need to look at each item and analyze it to see if it makes sense in Go.
The first point to consider is the name itself. Calisthenics comes from the Greek and means a series of exercises to reach an end, such as improving your physical fitness. In this context, the exercises improve the conditioning of our code. The problem is the Object term, because this concept does not exist in Go. Therefore, I propose a first change: from Object Calisthenics to Code Calisthenics. I leave the comment area below to discuss if this is a good suggestion.
Let’s analyze the other items.
One level of indentation per method
Applying this rule allows our code to be more readable.
Let’s apply the rule to the code:
One result, far more readable, would be:
Don’t use the ELSE keyword
The goal of this item is to avoid using the else keyword, generating a cleaner and faster code because it has fewer execution flows.
Let’s look at the code:
We can apply the Early Return concept and remove the else from the function Login:
Wrap all primitives and Strings in classes
This rule suggests that primitive types that have behavior must be encapsulated, in our case, in structs or types and not in classes. That way, we encapsulate the behavior logic and make the code easy to maintain. Let’s see the example:
Applying the rule, using structs:
Another possible and more idiomatic refactoring, using types could be:
Besides being readable, the new code allows easy evolution of business rules and greater security in relation to what we are manipulating.
First class collections
If you have a set of elements and want to manipulate them, create a dedicated structure for that collection. Thus, we can implement all the related behaviors to the collection in the structure, such as filters, validation rules, and so on.
So, the code:
Can be refactored to:
One dot per line
This rule states you should not chain functions, but use only those that are part of the same context.
We will refactor to:
This rule reinforces the use of “Law Of Demeter”:
Only talk to your immediate friends
This rule does not apply directly to Go. The community has its own rules for creating variable names, including reasons for using smaller ones. I recommend reading this chapter of: Practical Go: Real world advice for writing maintainable Go programs
Keep all classes less than 50 lines
Although there is no concept of classes in Go, this rule states that entities should be small. We can adapt the idea to create small structs and interfaces that we can use, via composition, to form larger components. For instance, the interface:
Can be adapted to:
Thus, other interfaces and scenarios can reuse the Reader and Writer.
No classes with more than two instance variables
This rule does not seem to make sense in Go, but if you have any suggestions, please share.
Like the previous rule, this also does not seem to be adaptable to Go, because it is not a pattern used by the community, as seen in this topic of Effective Go. But suggestions are welcome.
Applying these rules, among other good practices, it is possible to have a cleaner, simple and easy to maintain code. I would love to hear your opinions on the rules and these suggestions of adaptation to Go.
https://javflores.github.io/object-calisthenics/ https://williamdurand.fr/2013/06/03/object-calisthenics/ https://medium.com/web-engineering-vox/improving-code-quality-with-object-calisthenics-aa4ad67a61f1
I have adapted some examples used in this post from the repository: https://github.com/rafos/object-calisthenics-in-go
Thank you Wagner Riffel and Francisco Oliveira for suggestions on how to improve the examples.
Originally published at https://eltonminetto.net.