The “new thing” when I first started working with computers was Structured Programming. One of the tenets was that a function should have exactly one entry point and exactly one exit point. Some languages (Pascal, for example) enforced this by not having a return statement that would allow you to exit the function from somewhere in the middle.
In retrospect, that probably was a good thing, because people used to do all kinds of things like jump into the middle of a function, exit a function without returning a value, etc. Restricting functions to a single entry point and a single exit point was an effective way to prevent those stupid programmer tricks. But the restrictions are a little too restrictive.
I can’t think of any reason for a function to have multiple entry points, but restricting a function to a single exit point leads to things like this:
public boolean isExternal(final URI url) {
boolean target = false;
if (url.isAbsolute()) {
// this can be expensive and slow, run only for absolute URLs which should be an exception
final String serverName = requestAttributesProvider.get().getServerName();
final String host = url.getHost();
if (!host.equals(serverName)) {
target = true;
}
}
return target;
}
This code has an unnecessary intermediate variable, and an unnecessary level of nesting. All due to the restriction on the number of exit points. If we relax that restriction and allow an early out, the code becomes less cluttered:
public boolean isExternal(final URI url) {
// this can be expensive and slow, run only for absolute URLs which should be an exception.
if (!url.IsAbsolute()) {
return false;
}
final String serverName = requestAttributesProvider.get().getServerName();
final String host = url.getHost();
return !host.equals(serverName);
}
The idea here is to make a decision and then get out. There’s no reason to clutter the rest of the code with the “it’s not an absolute url” context. That allows us to eliminate the target variable, and reduces nesting in the function.
I find that code to be much more clear than the original.
A purist, by the way, would split that into two functions:
public boolean isExternal(final URI url) {
// this can be expensive and slow, run only for absolute URLs which should be an exception
if (!url.IsAbsolute()) {
return false;
}
return urlHostisExternal(url);
}
private boolean urlHostIsExternal(final URI url) {
final String serverName = requestAttributesProvider.get().getServerName();
final String host = url.getHost();
return !host.equals(serverName);
}
On a more general note, this is an anti-pattern:
if (something) {
return true;
}
else {
// do other stuff
}
return whatever;
If you return from inside the if block, then the else is implied. There’s no need for it. You can reduce nesting and increase legibility by eliminating it:
if (something) {
return true;
}
// do other stuff
return whatever;
I can’t think of any case in which such a transformation doesn’t make the code more readable, especially when working with cascading or nested if structures.
The same kind of thing applies to loops. Say you’re doing a sequential search for an item in a list. Structured Programming purists would have you write something like this:
bool found = false;
int ix = 0;
while (ix < list.length && !found)
{
if (list[ix] == thingToFind)
{
found = true;
}
++ix;
}
if (found)
{
return ix;
}
else
{
return -1;
}
We can eliminate a lot of clutter by returning directly from within the loop:
int ix = 0;
while (ix < list.length)
{
if (list[ix] == thingToFind)
{
return ix;
}
++ix;
}
// here, we know that the item wasn't found, so just return sentinel value.
return -1;
Again, I can think of no case in which that transformation doesn’t improve readability.
Long-time readers probably wondered why some of the code examples above are in Java. Most of the code I’ll be working on in my new job is Java, so I figured it’d be a good idea to start using it. I’ll still be working with C# in my home projects, and reporting on them here, but there also will be Java from time to time.