Some years ago I got the habit of creating explaining variables not only to extract parts of complex expressions, but also to give proper names to the arguments of function/method calls (in the absence of named arguments). In my opinion, it makes it easier to understand the intention of what is passed, without the need to check the argument names in the IDE; you can read them even with a text editor. I've tried to mitigate their need using enumerations and constants where possible. But very often it's impossible, so I'm forced to create these temporary variables. Specially when I call methods that belong to other levels of abstraction and a variable has a different purpose in that context (typical when you use any API).
This strategy has some caveats though. The most important is that it adds more and more lines of code, forcing me to extract methods even for very simple constructions, where people usually had wrote 1-2 lines at most. The second one is that, if you re-factor the method and change its name or the name of an argument, the corresponding variable name isn't in synchrony with it. So you've to inspect the code to find these mismatches and fix them by yourself.
So I want to know what policy would you choose:
a. Forget about this idea and let the IDE show you the argument names. Most people use an IDE after all. Only use explaining variables in cases in which the IDE can't help you.
b. Keep this strategy but avoid it when you use certain APIs of the Java core, which you're supposed to have memorized and to known well. Basically java.lang.*, java.util.* and java.math.*.
c. Keep it as is.
I've checked some projects at GitHub, from people who tell that they write self-documenting code, to have another perspective. But most of them don't do this. They nest method calls very often and use "magic numbers", even with APIs that aren't from the Java core. Without making clear the intention of the passed value. I guess they rely on their IDEs (option "a").
I agree that this is a good idea. In general, it's better to write code that's easier to read, because that will make it easier and cheaper to maintain and will lower the chance that you'll have a bug.
On the other hand, you don't have to overdo it. For methods that are often used, and of which everybody knows what they mean, it's not really necessary to do this.
So I'd choose option b.
Jesper de Jong wrote:So I'd choose option b.
@Avor: And assuming that Jesper is right in his assumption, I'd even violate option b on some occasions.
For example: breaking out complex method chains into variables is good for another reason - and this applies even if you are using an IDE - it makes debugging much easier.
Avor Nadal wrote:This strategy has some caveats though. The most important is that it adds more and more lines of code, forcing me to extract methods even for very simple constructions, where people usually had wrote 1-2 lines at most.
Consolidating method calls can be a good idea, but there is no hard rule that doing something in one or two lines is always better than doing the same thing in more lines. Your reasoning on this seems sound to me: code that is composed of more lines can be easier to understand than code that is composed of fewer lines, if you choose the right places to break those few lines up. (Winston's point about debugging applies here, too.)
I've checked some projects at GitHub, from people who tell that they write self-documenting code
No such thing.
Right now, I use option C. But I'm feeling that I may be taking this too far. For example, I use this strategy even with these methods:
Time ago I followed the option B, but then I had doubts about when was appropriate and when it wasn't, because I have no idea about how much an arbitrary programmer knows :S .
* The API is not well known or I don't know it well
* Without the extra variables, the line becomes long and confusing
So for me, I wouldn't for the substring example, but if the indices were a little more complex I probably would.
- X 2
So like most things in engineering, it's a balance along a spectrum.
One thing you might want to consider is using the Builder pattern. This is particularly useful for making constructor calls clear, but I also saw someone at Devoxx who adapted this pattern to implement something similar to named parameters for method calls. That is probably overkill in most situations, but could potentially make some code easier to read.
I also believe that named arguments would be very helpful, if they were used with moderation. I guess that the Java engineers don't want to add them to prevent that people abuse them. Because people could pass lots of arguments to a single function instead of creating proper classes. Named arguments also have other complex implications. But still, it could be a good feature.
Another possibility to give names to arguments is to use short comments, but it makes the code look like "ugly" and make it harder to read:
There is also another alternative, consisting in building a dedicated class with static methods that simply bypass the passed arguments. Then, these methods can be imported statically and used to give a name to some arguments:
I want to clarify that these examples are about giving names to arguments, not about ways to emulate named arguments. The pass of values is still positional, of course.
I also think that the Builder Pattern is useful. But for existing APIs or for very specific situations I believe that it's overkill. Because you would (almost) need a different builder for each method.
Avor Nadal wrote:Another possibility to give names to arguments is to use short comments, but it makes the code look like "ugly" and make it harder to read:
I hope you don't mind me saying, but I fear you may be "overthinking" this.
I'm all in favour of readable code, and I try very hard to make mine so; but I'm not teaching Java 101, so I assume that anyone reading my code is:
(a) A Java programmer.
(b) Moderately intelligent.
(c) Knows how to look up the API docs.
and if they can't work out that a StringBuilder(int) constructor might be in order to supply an initial capacity, then they really shouldn't be reading it.
But if you really feel that it needs further clarification, or that your "initial capacity" expression is so complex that it might confuse, there are two possible solutions:
int initialCapacity = fooList.size () * 2;
StringBuilder result = new StringBuilder(initialCapacity);
StringBuilder result = new StringBuilder();
After all, StringBuilder(int) is simply an optimization.
Don't forget that all this spoon-feeding comes at a cost - not the least of which is that your audience may start to rely on it.