Repeated validation could mean it’s time for an object rather than a primitive

It’s very easy to take “Don’t Repeat Yourself” (DRY) to an illogical extreme. It’s also possible to take primitive avoidance in object-oriented computer programming languages (like C++ and Java) to an illogical extreme, something that I’ve written about before.

However, there are times when DRY can lead you to a more elegant solution, such as when it helps you see that restricting validation to object constructors is more elegant than repeatedly checking primitives for validity.

Elementary math will be enough to follow along the examples in this article even though they deal with a few unfamiliar mathematical concepts (which are not necessarily difficult).

I suppose I could have tried to come up with a toy example. I will make sure to explain the less familiar mathematical concepts that are necessary to understand entry validation in the program I’m drawing the examples from.

I’m working on this program that draws diagrams of prime numbers in various different sets of numbers called “rings.” My first version of the program dealt with “imaginary quadratic” rings only.

To get started, the program needs a negative squarefree integer, like −2 or −47. The default choice is −1.

A squarefree number is an integer that is not divisible by any perfect square greater than 1. It is easy to see that 2, 3, 5, 6, 7, 10 are all squarefree, while 4, 8 and 9 are not: 4 and 8 are both divisible by 4 = 2², and 9 = 3².

I assume that I don’t need to explain what negative numbers are. Anyone who studies object-oriented programming languages should have come across negative numbers when learning about the primitive data types.

So −2, −3, −5, −6, −7, −10 are also squarefree, but −4, −8 and −9 are not, since both −4 and −8 are divisible by 4, and −9 is divisible by 9.

What was originally called receives an primitive, which is checked for being negative, being squarefree, and not being below an arbitrary limit of −8191 (I had an even lower number as the original arbitrary limit).

Once verifies the primitive satisfies these requirements, it passes the along to the constructor, which also checks that the is negative and squarefree.

The arbitrary limit of −8191 does not apply to the constructor. The only reason you can’t use −2147483648 is because it’s divisible by 4, 16, 64, etc.

But you can use −2147483647 (which is ) for the constructor, though that wouldn’t lead to very interesting diagrams in ; I would need to come up with a different drawing algorithm, but that’s outside the scope of this article.

It feels redundant to have both the constructor and the constructor check that a given is negative and squarefree.

For at least a couple of reasons it would be wrong to assume that the constructor will always be given a proper initialization parameter.

So the constructor must do some validation. Then is it necessary for the constructor to do any validation at all? Perhaps only if I care to enforce the arbitrary −8191 limit.

Eventually it occurred to me that it just made a lot more sense for the constructor to receive an object and not do the same validation that the constructor does.

I’m not going to bore you with a complete history of the changes. The pertinent detail here is that the program needs to construct an object in order to construct a object.

However, there is still another bit of repeated validation elsewhere in the program: the end user can type in a dialog box a new negative squarefree number to select a different imaginary quadratic ring diagram to look at.

There is a big difference between the constructor and the dialog box for the user to enter the number: the former is normally called within the program, while latter is normally in response to the action of an end user who may not know and does not need to know how any of this works under the hood.

I wrote the dialog box subroutine to make a lot of quiet substitutions, like multiplying positive numbers by −1. Also, if the user enters a number that is not squarefree, like −162, the program will substitute the next lower squarefree number, like −163 (provided it’s not below the arbitrary limit of −8191, in which case it will substitute the arbitrary limit).

Like I said, the end user does not need to know how this works under the hood, and might not want to. It makes little difference to them if there is repeated validation or not, as long as it does not affect program performance in a way they can notice.

The dialog box subroutine could be rewritten so that instead of checking whether the user’s number is squarefree or not, it passes the number along to the constructor and catches an if that arises.

This could potentially mean trying to construct an object several times, and several objects would be constructed along the way.

The actual performance hit wouldn’t even be a hiccup. The worst case scenario, with the arbitrary limit of −8191, would be having to skip over five numbers, according to entry A045882 in the On-Line Encyclopedia of Integer Sequences (OEIS).

For example, if the user enters −844 (which is divisible by 4), the validation would skip over −844, −845 (divisible by 13²), −846 (divisible by 9), −847 (divisible by 11²) and −848 (divisible by 16) to finally land on −849, which is thrice −283.

That would be five failed attempts to create an object, and five successful creations of objects.

The program would display Z[√−849] at whatever zoom level happens to be set a lot faster than it would respond to an input of −3 at a zoom level of 2 pixels per unit interval, which takes a noticeable fraction of a second.

In this particular instance, I decided it was better to repeat the validation rather than rely on a constructor throwing exceptions, even though the performance hit is minuscule.

Even with the repeated validation of the user input elsewhere in the program, having one constructor rely on another constructor for validation is a more adaptable approach as I strive to explore algebraic integers in purely real quadratic rings, or at higher degrees, like in simple cubic real rings.

So I created the interface , which implements. In turn, both and extend (a textbook inheritance hierarchy).

Perhaps later on, as I figure out the math, I will write and , both of which will extend , an abstract class I’ve already started to write.

Along similar lines, I renamed as and made it extend the abstract class. There will definitely be , and maybe there will also be and , all of which extend .

The constructor requires an implementation of . The classes that extend can narrow that as needed, e.g., the constructor takes an object which it then passes up to the constructor (by invoking ).

Likewise the constructor would take a object which it passes up to the constructor.

Since the end user won’t generally be calling any of these constructors directly, the constructors can simply rely on the constructors of other objects to take care of the validation that is relevant to them instead of repeating it.

It’s different in the case of something that depends on user input. You certainly don’t want the program to just suddenly crash for an invalid input. If the program is not run from the command line, the end user might not see any exception messages.

But to repeatedly catch exceptions instead of just doing the necessary validation might be inelegant, even if the performance hit is negligible.

In summary, when you find the same kind of validation happening in different places in your program, you should seriously consider whether that validation might be better off restricted to a particular constructor.

Except perhaps in a toy example, the answer will be a judgement call that depends on your good taste.

is a composer and photographer from Detroit, Michigan. He has been working on a Java program to display certain mathematical diagrams.