A quick way to make a codebase more readable is to extract long or complex conditions / predicates to their own functions. The function’s name will explain to the reader what is being checked allowing her to move forward instead of trying to figure out the context or the calculations that are taking place.
The task
Completed tasks of the last two days
Lets assume that we have a piece of code that prints all tasks that were created in the last two days and are, as the business experts calls them, completed. Which means that are either Resolved
or Cancelled
.
We have two distinct concepts that can be extracted. One that will explain the date calculations and one that will force the business knowledge behind those two statuses.
createdInTheLastTwoDays()
The calculations can be extracted to a private function but since we are using Kotlin we will extract them to an extension function to make the code even more readable:
isCompleted()
Another benefit of having “the mentality of extraction” (yep, I just made it up) is that eventually we will come across a case like the completed
which is a business term that has not been transferred to the codebase!
So we don’t just make the code more readable but we improve our domain representation and force a rule that until now was known through documentations or, even worse, through conversations only.
The reader will not have to make any calculations while reading this code or trying to understand why only those statuses are being checked.
The code speaks for itself!
I always wonder in situations like these, what sort of thought process should one go through to decide whether a function should be made as an extension function or not? I could see myself making both “isCompleted()” and “createdInTheLastTwoDays()” an extension function, but you seem to have decided that “isCompleted()” should not be one. I would love to hear your thoughts on this.
LikeLike
First of all I still struggle with that but the general rule I follow is:
– If the function involves just the class and has no dependencies (either as parameters or return type) then I add it to the class. `isCompleted` is a good use case of that since it just checks the internal state of the instance.
– On the other hand if the function is to specific, like `createdInTheLastTwoDays`, or brings to the game other classes, then I prefer to make it an extension, limit its scope to the maximum and have it at the package/module that is needed. An easy example are all `toFoo(): Foo` functions.
LikeLike
Very good points. I will try to use the same thought process and see how it goes for me. Thank you very much for the article and your answer!
LikeLike