With This Ring I Thee Wed

marquise-engagement-rings-265150-1534099279390-main-700x0c

We’ve all seen some code that looks a bit like this.

String prefix = input.substring(0,2);
String delimiter = input.substring(input.length()/2, 1);
String postfix = input.substring(input.length()-2, 2);

String result = list.stream()
    .collect(joining(delimiter, prefix, postfix));
return result;

We were probably taught it was good, because we’re naming local variable and self-documenting what’s going on. I’ve even heard that those local variables are good because they’re places to linger on when you’re debugging.

Quick aside, don’t optimise for debugging, write unit tests that debug your code every goddamned build so you never have to run a debugger yourself!

Let’s look at the above from the point of view of the narrative, though.

  • There’s this prefix which is the first two characters of the input
  • There’s this delimiter which is the middle character of the input
  • There’s this postfix (suffix) which is the last two characters of the input
  • The result is the list joined with the things above
  • And we return it

The word order of this is unnatural. The temp variables are either hard to name, or named in a banal way which hides the what, but then inlining the what would completely suck.

Moreover, the above method is working at two levels of abstraction – its job is to create a processed result, which it ultimately uses a library for, but on the way it does some deeper more detailed calculations.

Software should read from left to right not requiring the reader to poke facts into their brain to inline into the code later. With this ring I thee wed should be refactored to I am marrying you using this ring. The code should be of the following form:

return list.stream()
   .collect(joining(
       ,
       ,
       
   ));

You read that as – I’m joining the list, with a delimiter, prefix and suffix, the details of which I can think about as I read from left to right.

But how do you do that without the splurge of code that makes it seem monolithic?

The answer is functional decomposition. Don’t give me dodgy variables, give me function calls.

return list.stream()
   .collect(joining(
       centralCharacter(input),
       naturalPrefix(input),
       naturalSuffix(input)
   ));

The moment you do this a few things happen:

  • You ask yourself what is this algorithm for the prefix
  • When you write the functions you’re better able to thing about unit testing them and any of their edge cases (strings shorter than 2 for instance)
  • You question why the algorithm is as it is
  • You give the reader the opportunity to dig deeper, or not

So, inline your variables, and replace here-and-now calculations with well named functions. Functions are named after their purpose not their implemented.

Warning: the substring function above is probably not correct, but illustrates the point.

To return to the subject of this post – With this ring I thee wed – which of the two forms below seems more natural to read now?

Ring thisRing = new Ring();
me.marry(thee, thisRing);

// or

me.marry(thee, new Ring());
Advertisements

One comment

  1. […] Before I write this, I should point out that I have often written code that makes use of an accumulator to aggregate data before returning it. Readers of this blog may note, however, that I’ve been very dismissive of the hanging temporary variable which seems to exist, only to hold a value up until its single return point. I’ve also talked about how declaring your variables reverses the reading order. […]

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s