Clean Code — Object Calisthenics in Javascript
Mostly, developers will spend their time working on the code. And to be able to work efficiently we need to understand the code. That’s why we can follow one of the good practices on Software Development called the Clean Code.
Clean Code is code that is easy to understand and easy to change
We do clean code because we know that code is written and will have changed from time to time, not by only us but also by the future developers. Because other developers also need to understand the code we have written, we need to make sure that our code can be easily understood by others. And essentially making life easier for everyone involved in our codebase.
People vector created by “pikisuperstar”
With many code reviews and many projects with different code conventions, of course, we need to have some rules that can be implemented across all codebase and help us to achieve clean code. And also as most high-level programming languages such as C, Java, Javascript can implement OOP or Object Oriented Programming concepts, We can create a codebase/project which follows both Clean Code and OOP concepts properly. It’s called Object Calisthenics.
Object Calisthenics are basically programming exercises, as a set of 9 rules to help us write better Object Oriented code
- 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
Keep in mind that these 9 rules are heavily oriented on OOP Concepts done in Java (before Java 8), therefore there are some of the rules probably cannot be applied “as is” in other programming languages and paradigms. If you already write code that is maintainable, readable, testable, and comprehensible, then these rules will help you write code that is more maintainable, more readable, more testable, and more comprehensible. Here, the provided example will be on Javascript ( Node JS ).
Rule 1: Only One Level Of Indentation Per Method
TL;DR If you have various conditions at different level, or a loop in another loop or a function which have more than one indentation, you can always simplify it or extract it
This rule will make us think about how to ensure that each method does exactly one thing — one control structure or one block of statements per method. If you have nested control structures in a method, you’re working at multiple levels of abstraction, and that means you’re doing more than one thing.
conditional structure in here are things such as if
or switch case
or ternary operation
if there are conditional structure inside other control structure, it is better to simplify it or extract it to a separated function
Example Rule 1 Conditional — Simplify the logic
In here, we can simplify the logic by merging line number 6 and line number 7 into
Ok, then what happens if we have a long if because we simplifying more than one of the nested conditional (ex: a && b && c
)? we can extract it to a boolean variable and give more context to it
...
const isLowHealth = healthPoint > 0 && healthPoint < 50;
if (isLowHealth) {
...
looping structure in here are things such as for
or for in/for of
or even iteration on the array, such as .forEach, .filter, .map and etc.
Example Rule 1 Loop — Extract method
we can extract the two for-loop inside to two more separated functions.
The benefit:
- by simplifying the conditional, it will be easier to understand
- by split it to more functions, we can give better naming and context as what the function does (better documentation)
- the new functions can be reused on other function
- give more clarity as we now have a single name for the index on every function instead of
index
,secondIndex
, andthirdIndex
Rule 2: Don’t Use The ELSE Keyword
TL;DR We can remove
else
keyword easily by using early return / guard clause
Well, are you surprised about this rule? yes, it is one of keyword that we will learn first when jumping to programming. Actually, we never need an ELSE keyword. An easy way to remove the else
keyword is to rely on the early return solution. Other solutions which a little bit hard are the polymorphism concept or State Pattern or Strategy Pattern or Null Object Pattern or you can also read how to do it with an object from here
Example Rule 2 — Early Return
A guard clause is a snippet of code at the top of a function or method that returns early when some precondition is met. Therefore, we can make sure the lower code or blocks outside the if will not be called. We can also do if-block on top to eliminate condition using return/throw, so it won’t go down to execute the main part of the function.
If we refactor it to do early return, it will become this:
Benefit:
- reduce complexity (or increase the readability of the logic flow)
- reduce unwanted variables if previously you do only a single return on the last part of the function.
- possibly reduce duplication on more complex if / conditional logic
Rule 3: Wrap All Primitives And Strings
TL;DR every variable with type that was not written by yourself is primitive, and you should encapsulate them to a class by their behaviours.
This rule focus on avoiding one of the code smells Primitive Obsession. Every variable which is a primitive data type like integer, float, string, array, map, or object literal ( {} ) even in a dynamic-type language like javascript, we need to wrap them to a class / more classes, we can also combine two or more variable which has related behavior and can represent a real-world things/concepts inside a class. Let’s take this example
kilometer and meter can be represented to a real-world example — Distance. And we can create a class to represent the value, and give the proper properties and behavior.
We can go further by wrap things like the “kilometer” string to a class called Unit, and if things like kilograms, grams, or any other measurement need to be used on our code, we can also make a more general class called Measurement instead of Distance.
Benefit:
- give better context with additional information about what the value is and why it is being used
- enforce encapsulation and abstraction
Rule 4: First Class Collections
TL;DR Every collection should be contained or wrapped in its own class
This is still related in rule 3 to wrap all primitives. Collections in javascript are like Array -[]
, Map or Set, or other Data Structures which are possibly created by some other library. We need to make sure every class that has a collection shouldn’t have any other members inside the class. If this happens, we need to make them to a new class and every possible action/behavior that we need on the collections (push, remove, filter, sort, etc) should be put as methods of this first-class collection. For the naming part, instead of giving its name as the plural form of what object that it can hold (Person & Persons), we can give a better name that represents its domain.
Let’s say we have a Player class.
And then we have Match class
And this is how we use Player and Match class in the main class
You can see that in Match class we have a member of the collection called terrorists
but also another member which is a collection called counterTerrorists
. Both of them are holding Player object. By following the rule, we need to extract this array of Player, to a new class that only hold this array, the best for this context is a Team just like how we called it in-game, Terrorist team and Counter-terrorist team. So here is the updated Match class after we change the primitive collection to the first-class collection of Team.
Here’s our new Team class that hold of the array of players, we also put the related behavior of the array which are the addition of a member and the sorting of the members into methods of add
and sortByKillCountDescending
Benefit:
- possibly prevent duplication of code related to the collections
- enforce encapsulation
Rule 5: One Dot Per Line
TL;DR you should not chain method calls which doesn’t have the same context or return type
Let’s see this example directly
We can see that line number 4 is actually long. Method calls that are too long can distract programmers as they will not know where that long method calls can fail or throw errors, and that’s why we need to split it. So by adding enter on every dot is the way to fix this? The answer is NO, we split the long method calls to a new variable And you only can do the chaining method only if you do Method Chaining Pattern (e.g. a Query Builder). We can split them into a new variable if the chaining methods don’t return the same type or not having the same context. Here is some example of how we can find a way to correctly split it
rawText.trim() // return string. AcceptablerawText.trim().toUpperCase() // return string. Still AcceptablerawText.trim().toUpperCase().replace('YOU', username) // return string. Still AcceptablerawText.trim(),toUpperCase().replace('YOU', username).split(' ') // return array, has violated one dot per line rule
let’s now split the dot which still returns strings to a new variable
const textWithUsername = rawText.trim().toUpperCase().replace('YOU', username);
continue with the one dot per line check
textWithUsername.split(' ') // return array. become AcceptabletextWithUsername.split(' ').reduce((result, item) => ({ ...result, [item]: item }), {}); // return object, has violated one dot per line rule
by again splitting the dot which still returns an array to a new variable, the final code of this will become:
Ok, if you find it’s still too long, you can follow some convention that you can put enter after every dot just to prettify the code.
Benefit:
- better to track for errors / easier to debug
- can put better naming for the split variables (increase readability)
Rule 6: Don’t Abbreviate
TL;DR put better naming for your variable / class, don’t abbreviate them
We used to put an abbreviated name for the variable, in projects, it will sometimes get harder to know what the abbreviation stands for and you even put more meaningless variables like x, n, t like in competitive programming format. The code you write will be used by others, and we should make sure they also understand it. It won’t hurt us to take one or more extra minutes to give better naming for them.
in here, we can change temp, i and n as it is abbreviated, and change it to this:
Benefit:
- reveal the intention of the variable
Rule 7: Keep All Entities Small
TL;DR stick to choose rules like function with less than 10 or 15 lines and also classes with less than 50 lines. Start to always refactor your code based on each function / class responsibility (SRP — Single Responsibility Principle)
The actual rule is “No class over 50 lines and no package over 10 files”. But if you have tried to follow other rules, you will always have a small class/function as a result. So, if you have stumbled upon some of your code has more than 15 lines on one function or more than 50 lines on one class. It is a sign that you should start implementing the other rules. Or even start thinking about how to implement some software design patterns on your code. Things to note here is if it’s more than what the rule said because you have many lines of boilerplate code on it, it’s your own call whether to fully stick with these rules or not.
Benefit:
- help other developers to not become overwhelmed with too many lines of code in one file ( it won’t scare them to change it / refactor it)
- less of class with no specific behavior / single responsibility (God Class / Blob Class)
Rule 8: No Classes With More Than Two Instance Variables
TL;DR decompose all of your classes and make each of them to only have two instance variables / attributes (or state / props in some framework)
Yes, I know this will be hard for us to stick with this rule in a class/object whose main purpose is to save/show data like Entity / DTO in java. So, it’s your own call to how many minimum instance variables that a class can hold, maybe stick with only three? The tip to follow this rule is to take two of the instance variables that are related to each other and can be extracted out with a responsibility/behavior or have a real-world representation, wrap it into a class.
If choosing from real-world representation on the usual way of parents give a name to their child, first and the middle will be a given name ( or even a third/fourth name), and the last name will be a surname or inherited family name. We can change it to this:
This way, we can even put the third or fourth name of it as GivenName accept more than one parameter on its constructor, the usage example is:
const givenName = new GivenName('Davidsen', 'second', 'third');
const surname = new Surname('Satio');
const Name = new Name(givenName, surname);
Benefit:
- help to create a class which we can add more functionality on it as we have cleared out what responsibility it has ( open-closed principles )
- having complex large objects become much simpler models.
Rule 9: No Getters / Setters / Properties
TL;DR Tell Don’t Ask, remove all your setters and getters, start creating functions that process and return the data that you need.
This rule is the most controversial of all rules because we usually put getters/setters when we learn basic OOP. This habit continues by us always using these getters/setters everywhere just to do simple things, and sometimes, it even breaks encapsulation.
To eliminate this habit, we need to force the new habit of providing functions that make sense. Tell Don’t Ask is the key, If we have found that we do a job/decisions outside an object by getting its state, we should move it inside the object who originally hold the state. Any decisions based entirely upon the state of one object should always be made ‘inside’ the object itself.
To try to implement this rule, we can first start by eliminating all the setters. If you want to set the initial value of a state/properties of a class, you can put it on the constructor. On line 20, we want to add 2000 to the wallet balance, which made the decision on getting the last value by using getter and then add 2000 outside the wallet class. Instead, we can create a function on the wallet which are more straight-forward
increase(money) {
this._balance += money;
}
Ok. now we need to check about getters. it’s ok to use getters to get the state of an object, but only if it’s needed by other parts/class. If you want to check whether the value of the object is the same with another object, you can introduce .equals
. In this context, things left is to print out the last state of the wallet (with its balance), for this, we can actually introduce a .toString()
function to print out the last state of the balance. The final code then will become this:
Benefit:
- not breaking encapsulation of a class
- significant reduction in duplication
- open for more chances to implement new features.
- keep in check on the read-only state of the class
- no overused of setters/getters, only keeping the needed one based on the requirement
Conclusion
If you don’t feel comfortable with these rules at first, it’s ok, you can start at focusing to implement the rule one by one, instead of trying all of them directly. Following these rules with discipline will force you to come up with the harder answers that lead to a much richer understanding of object-oriented programming. Some developers will always claim that clean code is a waste of time but in the long run, you will be grateful that you have avoided many bad things that can happen. Think it like this 😂:
Always code as if the one who ends up maintaining your code will be a violent psychopath who knows where you live — John Woods
I’d love to hear your thoughts on Clean Code and how it changed your perspective 😏 !
This article is part of my collaborations with my co-worker about Clean Code:
PART 1 — Clean Code — 4 Rules Of Simple Design
PART 2 — Clean Code — Object Calisthenics in Javascript [ Current ]
These sources are my anchors to write about this concept, as some of the words are compiled from these respectable sources:
- Clean Code: A Handbook of Agile Software Craftsmanship
- https://cvuorinen.net/2014/04/what-is-clean-code-and-why-should-you-care/#:~:text=As%20Robert%20C.,paid%20appropriate%20attention%20to%20details.
- https://williamdurand.fr/2013/06/03/object-calisthenics/
- https://www.cs.helsinki.fi/u/luontola/tdd-2009/ext/ObjectCalisthenics.pdf
- Thoughtworks Anthology (1st edition)