When I'm trying to create an interface for a specific program I'm generally trying to avoid throwing exceptions that depend on non-validated input.
So what often happens is that I've thought of a piece of code like this (this is just an example for the sake of an example, don't mind the function it performs, example in Java):
public static String padToEvenOriginal(int evenSize, String string) {
if (evenSize % 2 == 1) {
throw new IllegalArgumentException("evenSize argument is not even");
}
if (string.length() >= evenSize) {
return string;
}
StringBuilder sb = new StringBuilder(evenSize);
sb.append(string);
for (int i = string.length(); i < evenSize; i++) {
sb.append(' ');
}
return sb.toString();
}
OK, so say that evenSize
is actually derived from user input. So I'm not sure that it is even. But I don't want to call this method with the possibility that an exception is thrown. So I make the following function:
public static boolean isEven(int evenSize) {
return evenSize % 2 == 0;
}
but now I've got two checks that perform the same input validation: the expression in the if
statement and the explicit check in isEven
. Duplicate code, not nice, so let's refactor:
public static String padToEvenWithIsEven(int evenSize, String string) {
if (!isEven(evenSize)) { // to avoid duplicate code
throw new IllegalArgumentException("evenSize argument is not even");
}
if (string.length() >= evenSize) {
return string;
}
StringBuilder sb = new StringBuilder(evenSize);
sb.append(string);
for (int i = string.length(); i < evenSize; i++) {
sb.append(' ');
}
return sb.toString();
}
OK, that solved it, but now we get into the following situation:
String test = "123";
int size;
do {
size = getSizeFromInput();
} while (!isEven(size)); // checks if it is even
String evenTest = padToEvenWithIsEven(size, test);
System.out.println(evenTest); // checks if it is even (redundant)
now we've got a redundant check: we already know that the value is even, but padToEvenWithIsEven
still performs the parameter check, which will always return true, as we already called this function.
Now for isEven
of course doesn't pose a problem, but if the parameter check is more cumbersome then this may incur too much cost. Besides that, performing a redundant call simply doesn't feel right.
Sometimes we can work around this by introducing a "validated type" or by creating a function where this issue cannot occur:
public static String padToEvenSmarter(int numberOfBigrams, String string) {
int size = numberOfBigrams * 2;
if (string.length() >= size) {
return string;
}
StringBuilder sb = new StringBuilder(size);
sb.append(string);
for (int i = string.length(); i < size; i++) {
sb.append('x');
}
return sb.toString();
}
but this requires some smart thinking and quite a large refactor.
Is there a (more) generic way in which we can avoid the redundant calls to isEven
and performing double parameter checking? I'd like the solution not to actually call padToEven
with an invalid parameter, triggering the exception.
With no exceptions I do not mean exception-free programming, I mean that user input doesn't trigger an exception by design, while the generic function itself still contains the parameter check (if just to protect against programming errors).
padToEvenWithIsEven
does not perform validation of user input. It performs a validity check on its input to protect itself against programming errors in the calling code. How extensive this validation needs to be depends on a cost/risk analysis where you put the cost of the check against the risk that the person writing the calling code passes in the wrong parameter.