Improve Your Code With Object Calisthenics
IT Tips & Insights: A Softensity Software Engineer details how to write cleaner code through the rules of Object Calisthenics.
By Caio Vidal, Software Engineer
Object Calisthenics was defined by Jeff Bay in his book, “The Thoughtworks Anthology.” It defines 9 different rules that you can use to write your code and keep it clean. Note that Object Calisthenics is not a design pattern. It defines simple rules that you can apply focusing on the maintainability, readability, and comprehensibility of your code. So, you’re free to apply only those you find relevant.
- Only one level of indentation per method
- Don’t use the else keyword
- Wrap all primitives and strings
- First-class collections
- One dot per line
- Don't abbreviate
- Keep all entities small
- No classes with more than two instance variables
- No getters / setters / properties
Only one level of indentation per method
Indentation is helpful to organize your code and nest all statements in our logic. However, too much indentation makes the code much harder to read and understand.
Here is an example:
As we can see we have 2 levels of indentations (on the first and second “for”) which makes it hard to follow the code. Rather than having the two “for” inside the method, we can move them to separate methods.
It will not reduce the number of lines, but will improve readability.
Don’t use the else keyword
The if/else construct is very popular in nearly every programming language. You’ve probably seen a nasty nested conditional that is impossible to follow without spending some time on it. It is all too easy to simply add another branch to an existing condition instead of refactoring it to a better solution.
Here is an example:
A simple way we can avoid the ELSE keyword is by making use of the early return pattern. When a condition is met, return the appropriate value. That way we can fail fast and avoid confusing conditional logic which is hard to read and understand.
In the example above, we moved the else conditions to a separated if statement with an early return.
Wrap all primitives and strings
This rule is easy. It requires you to avoid primitive obsession code smell. The main goal is that in certain scenarios, rather than having raw primitive types you should have a meaningful one.
Let’s jump into an example of a vehicle registration plate:
In this case, we should ask if the NumberPlate type holds more information than just its value. In this scenario, the answer is yes, it does.
Let’s see a different way of this type that time having a class rather than a raw primitive type.
As we can see now the type is turned into a class that holds useful information regarding the “numberplate” parameter. Having a dedicated type can help you apply unit tests to it and easily create test flows.
First class collections
This rule is intended to enforce that any class that contains a collection shouldn’t have any other member variables.Each collection should be wrapped in its class and then any behaviors related to the collection will have a home.
Let’s see an example:
As you can see, we are mixing the collection rules in the main class. As an example, “AddCandy” method is maintaining a logic that doesn’t belong to the candy shot but rather to the stock itself.
So now let’s apply the rule:
We now have a different class to wrap the stock collection. It is only caring about what it is responsible for: maintaining the shop candies. Every single logic related to the stock will be added to this class and we can reuse this class in any other place. Here we are not only enforcing Single Responsibility Principle, but also improving the reusability of our logic.
One dot per line
This rule will help you to be aware of which object should take responsibility for an activity. Looking at lines of code with multiple dots is a signal that you have many misplaced responsibilities, and the activity is happening in the wrong place.
Let’s jump into an example:
The line in the “ShowCurrentPieceInLower” method looks like having too many things to care about. We are likely not only violating SRP principle but also letting the method manipulate a field from Piece to which the class has no direct access. A better way would be to add the right responsibilities to each class like in the example below.
Now we are not exposing the fields from Location and Piece and each class has its activities. The “ShowCurrentPieceInLower” method is not only worried about calling the method within the Location class.
This one is straightforward. How many of you have seen abbreviations that make no sense to you? Abbreviations may seem like the right thing to do when you have long method or property names, but that is incorrect. Most of the time it is quite useful to have long method names rather than abbreviated small ones.
Looking at the above example we don’t know exactly what “P” from the “CreatePFromName” method stands for without looking at the parameters.
Rather, you can have:
Here we have long methods which make it much easier to understand. However, we still have some problems.
- Looking at the name of the “EvaluateSumAndPrintResultInConsole” method we can see that it is probably violating SRP principle.
- We also probably have code duplication in the last two methods.
In the first case, we should have two separated major components: one for operation and the other one for displaying the value. This logic shouldn’t be mixed into a single method.
For “CreatePersonFromName” and “CreatePersonFromLocation” the creation of the “Person” object should be moved to its base class. I just want to reinforce that abbreviations are usually a red flag and the same applies to business names.
Keep all entities small
Let’s say you just started on an existing project with complex business logic to understand. Ok, you have been told that the “CustumerService.cs” is a good entry point, to begin with. Everything looks good except the fact that you see the scrolling bar shrinking, and your editor is showing more than 1k lines of code. This is not only hard to read and understand, but also hard to either add new code or maintain existing ones.
This is also a signal that the maintainers are not careful enough and may be skipping the core OOP principles and deteriorating the code base. As a goal, we can think of a limit of lines per class like a minimum of 50 lines and a maximum of 150. Of course, it all depends on your scenario, but I believe this is a good reference to start.
No classes with more than two instance variables
This rule is more like a metric to keep you aware of keeping a high cohesion and a better encapsulation of your objects. Adding a new instance variable to a class immediately decreases the cohesion of that class. If you have more than two instance variables in your class, you must start thinking about decomposing your instances into a new class.
No getters / setters / properties
Having public getters and setters may violate the encapsulation of your logic, letting any external code change the state of your object.
Let’s see an example:
As we can see here, any external code could add or reduce the amount of money of your candy shop. Of course, we don’t want that. Instead, we want to have the Money property maintained by the class itself and let it be accessible externally for read-only.
It would look like this:
So now we have a candy shop that is responsible for the logic of buying and selling candies. External code will change its state by using the new methods.
Although the rules that we described here are intended to improve your code, you don’t need to blindly follow them. That said, you should at least ask yourself and your code why they are being violated.
Hi everyone! I’m Caio. I've been a software engineer for more than 9 years. I have worked for companies in Brazil and abroad. I enjoy designing and implementing new projects, focusing on aligning the IT deliveries with the business' needs. My primary aim is to continue learning and developing my programming skills in order to develop better software. I enjoy programming enough to do it in my spare time.