Quando cerco di creare un'interfaccia per un programma specifico, in genere cerco di evitare di lanciare eccezioni che dipendono da input non convalidati.
Quindi quello che succede spesso è che ho pensato a un pezzo di codice come questo (questo è solo un esempio per il gusto di un esempio, non importa la funzione che esegue, esempio 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, quindi supponiamo che evenSize
sia effettivamente derivato dall'input dell'utente. Quindi non sono sicuro che sia pari. Ma non voglio chiamare questo metodo con la possibilità che venga lanciata un'eccezione. Così faccio la seguente funzione:
public static boolean isEven(int evenSize) {
return evenSize % 2 == 0;
}
ma ora ho due controlli che eseguono la stessa convalida dell'input: l'espressione nell'istruzione if
e il controllo esplicito in isEven
. Codice duplicato, non bello, quindi facciamo il refactoring:
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, questo l'ha risolto, ma ora entriamo nella seguente situazione:
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)
ora abbiamo un controllo ridondante: sappiamo già che il valore è pari, ma padToEvenWithIsEven
esegue ancora il controllo dei parametri, che restituirà sempre true, come abbiamo già chiamato questa funzione.
Ora per isEven
ovviamente non pone un problema, ma se il controllo dei parametri è più ingombrante, questo potrebbe comportare costi eccessivi. Oltre a ciò, eseguire una chiamata ridondante semplicemente non sembra giusto.
A volte possiamo aggirare questo problema introducendo un "tipo convalidato" o creando una funzione in cui questo problema non può verificarsi:
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();
}
ma ciò richiede un pensiero intelligente e un refactoring piuttosto grande.
Esiste un modo (più) generico in cui possiamo evitare le chiamate ridondanti a isEven
ed eseguire il controllo dei doppi parametri? Vorrei che la soluzione non chiamasse effettivamente padToEven
con un parametro non valido, che attiva l'eccezione.
Senza eccezioni, non intendo programmazione senza eccezioni, intendo che l'input dell'utente non innesca un'eccezione in base alla progettazione, mentre la stessa funzione generica contiene ancora il controllo dei parametri (se solo per proteggersi dagli errori di programmazione).