Cuando intento crear una interfaz para un programa específico, generalmente trato de evitar lanzar excepciones que dependen de entradas no validadas.
Entonces, lo que sucede a menudo es que he pensado en un fragmento de código como este (este es solo un ejemplo por el bien de un ejemplo, no importa la función que realiza, ejemplo en 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();
}
Bien, digamos que en evenSize
realidad se deriva de la entrada del usuario. Así que no estoy seguro de que sea uniforme. Pero no quiero llamar a este método con la posibilidad de que se produzca una excepción. Entonces hago la siguiente función:
public static boolean isEven(int evenSize) {
return evenSize % 2 == 0;
}
pero ahora tengo dos comprobaciones que realizan la misma validación de entrada: la expresión en la if
declaración y la comprobación explícita isEven
. Código duplicado, no agradable, así que refactoricemos:
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, eso lo resolvió, pero ahora nos metemos en la siguiente situación:
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)
ahora tenemos una verificación redundante: ya sabemos que el valor es par, pero padToEvenWithIsEven
aún así realiza la verificación de parámetros, que siempre devolverá verdadero, como ya llamamos a esta función.
Ahora, por isEven
supuesto, no plantea un problema, pero si la verificación de parámetros es más engorrosa, esto puede generar un costo excesivo. Además de eso, realizar una llamada redundante simplemente no se siente bien.
A veces podemos solucionar este problema introduciendo un "tipo validado" o creando una función donde este problema no pueda ocurrir:
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();
}
pero esto requiere un pensamiento inteligente y un refactor bastante grande.
¿Hay alguna forma (más) genérica en la que podamos evitar las llamadas redundantes isEven
y realizar una verificación de doble parámetro? Me gustaría que la solución no llame realmente padToEven
con un parámetro no válido, desencadenando la excepción.
Sin excepciones, no me refiero a la programación sin excepciones, quiero decir que la entrada del usuario no desencadena una excepción por diseño, mientras que la función genérica en sí misma contiene la verificación de parámetros (aunque solo sea para proteger contra errores de programación).
padToEvenWithIsEven
no realiza la validación de la entrada del usuario. Realiza una verificación de validez en su entrada para protegerse contra errores de programación en el código de llamada. La extensión que debe tener esta validación depende de un análisis de costo / riesgo en el que se compara el costo de la verificación con el riesgo de que la persona que escribe el código de llamada pase el parámetro incorrecto.