Saturday, June 11, 2016

The name should express the intention

This time I will start with a code sample. Take a look at this:
if (code.isComplexOrUnreadable()) {
    refactor(code);
}

Can you tell me what is wrong with this code? No?

Let me ask you another question then. How do you think the implementation of isComplexOrUnreadable() method looks like?
I assume that many of you would imagine something similar to this:
boolean isComplexOrUnreadable() {
    return complex || unreadable;
}

Am I right?

Ok, so let me ask you again: can you tell me what is wrong with this code?

The first thing regards duplication of information, which should be now obvious for you. The second thing relates to the lack of information. How is it possible, you may ask? Let me explain.

Don’t Repeat Yourself

We repeat information about the way method was implemented. This is definitely something we should avoid.

Do you remember the DRY principle? We won’t repeat the code, because if we do so, we will have to remember to update two or more places in case of any change.

What would happen if, after some time, we decided to change the implementation of the method body?
boolean isComplexOrUnreadable() {
    return (complex || unreadable) && tests.exist();
}

Would it be ok to leave the same name of the method? No, because the name would become misleading. The name of the method would give you invalid information.
Any change in implementation would always affect the name of the method. So in our case we would have to rename the method into something like this:
boolean isComplexOrUnreadableWithTests() {
    return (complex || unreadable) && tests.exist();
}

But wait! Now it is still a little bit confusing because after reading the name of the method we may have a false impression that implementation looks like that:
return complex || unreadable && tests.exist();

Well, as you can see even kind of a “correct” name can be misleading in the situation when the name reflects implementation.

The missing Why!

In the previous paragraph I wrote about the possible change in the method body. But when the name reflects implementation, it makes introducing modification in the particular method extremely difficult. Not because of the code itself, but because of… yes, a wrong name. Why is it like that?

Each change in the code needs to be justified, there must be a reason behind each change. How can you know that you have to modify this particular method if there is no visible relation between the implementation and the reason. How could you know whether this would be a right place to introduce a change? Maybe it is not. How could you know?

What kind of change would be a good reason to modify this method:
boolean isComplexOrUnreadable() {
    return complex || unreadable;
}

What question am I asking with this method? Only the question related with its implementation. There’s no question that I may connect with any new requirements.
In that case I will need to look into the code, look at the place where I think the change should be made and I will need to analyse the code to find the right place. Hopefully I will succeed.

Reusability

Another problem with such method is that probably we won’t use this part of the code in any other place. Even if we needed to have an answer on the same question. Why? Because there’s no chance that we would be able to connect this code with our question.
The question will be domain-related and the name reflects implementation. Until we start looking in a way aimed at finding out HOW to find an answer there’s no chance that we can anyhow link them together. The existing code with our current work.

The problem can get even bigger if there are many ways to get the answer, because we may choose different implementation.

What we are doing in such case? We are duplicating the logic. And what is event more problematic, those kinds of duplication can be extremely hard to find, because of different method/class names and potentially different implementations.

The Why you need

As you can see now, the name that describes HOW and not WHY can be a huge problem and can lead to other problems that degrade the quality of your code. This is not what we, as a professionals, would like to do.

To summarize, the name of the method or class that express intention of the code is a right direction, because:
  • You know what place should be modified if a change is needed.
  • You are not duplicating the same information (how the method was implemented).
  • You are not losing information what is the reason of method/class existence.
  • You know what question is answered by method’s invocation.
  • It is extremely easy to reuse the code in case the same question must be asked in different place.
  • You are not duplicating the code, because you simply don’t answer once again to the same question.

And at the end, this is how the code can look like if its name would express the intention:
if (code.canBeImproved()) {
    refactor(code);
}



No comments:

Post a Comment