DEV Community


Functions should have same level of abstraction

groundzerocro profile image AndroidPie Updated on ・2 min read

Yesterday I've been reading Clean Code, by Robert C. Martin, also known as Uncle Bob, and one of the rules really got my attention:

In order to make sure our functions are doing “one thing,” we need to make sure that the statements within our function are all at the same level of abstraction.

I'd go even step further, and apply this rule not only to a function, but also to a class or specific block of code. Now, this rule is quite important, and it's easy to ignore this code structure requirement. Mixing levels of abstraction within a function or a class is always confusing. Readers may not be able to tell whether a particular expression is an essential concept or a detail.

Switching between levels of abstraction makes code harder to read. While reading the code you have to mentally construct the missing abstractions by trying to find groups of statements which belong together.

Quote from principles-wiki

This rule requires a coder to have a discipline. For example, once you realise your class is missing a value, that is easily obtained by a .map function, you will be tempted to do the mapping in your root class like this.

class Presenter(private val repository: Repository) {
    fun getMembersAge() = getMembers().map(it.age)
    private fun getMembers() = repository.getMembers()
Enter fullscreen mode Exit fullscreen mode

This is wrong. In this case, there should be a class strictly responsible for that mapping, and work should be decoupled from the Presenter class. Root class, in our case Presenter class, should not know about the .map function being invoked.

class Mapper {
    fun map(members: List<Member>) =
Enter fullscreen mode Exit fullscreen mode
class Presenter(
    private val mapper: Mapper, 
    private val repository: Repository
) {
    fun getMembersAge() =
    private fun getMembers() = repository.getMembers()
Enter fullscreen mode Exit fullscreen mode

Code examples in this article do not represent real world examples. Presenter layer should not be injected with a mapper and naming is simplified.

This kind of rule, not only does make our code cleaner and more readable, but it also opens a lot of room for a quality unit testing and higher unit test coverage.

Discussion (0)

Editor guide