Quando provo a creare un'interfaccia per un programma specifico, in genere cerco di evitare di generare 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 a titolo di esempio, non importa la funzione che svolge, 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 dire che in evenSize
realtà è derivato dall'input dell'utente. Quindi non sono sicuro che sia pari. Ma non voglio chiamare questo metodo con la possibilità che venga generata un'eccezione. Quindi 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 if
nell'istruzione e il check-in esplicito isEven
. Codice duplicato, non carino, quindi cerchiamo di 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, questo 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 comunque il controllo dei parametri, che restituirà sempre vero, come abbiamo già chiamato questa funzione.
Ora, isEven
ovviamente, non rappresenta un problema, ma se il controllo dei parametri è più complicato, ciò può comportare costi eccessivi. Oltre a ciò, eseguire una chiamata ridondante semplicemente non sembra giusto.
A volte possiamo aggirare il 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 questo richiede un po 'di pensiero intelligente e un grande fattore di riflessione.
Esiste un modo (più) generico in cui possiamo evitare le chiamate ridondanti isEven
ed eseguire il doppio controllo dei parametri? Vorrei che la soluzione non chiamasse effettivamente padToEven
con un parametro non valido, innescando l'eccezione.
Senza eccezioni, non intendo una programmazione senza eccezioni, intendo che l'input dell'utente non attiva un'eccezione in base alla progettazione, mentre la funzione generica stessa contiene ancora il controllo dei parametri (se non altro per proteggere dagli errori di programmazione).
padToEvenWithIsEven
non esegue la convalida dell'input dell'utente. Esegue un controllo di validità sul suo input per proteggersi dagli errori di programmazione nel codice chiamante. L'ampiezza di questa convalida deve dipendere da un'analisi costi / rischi in cui si mette il costo del controllo rispetto al rischio che la persona che scrive il codice chiamante passi nel parametro errato.