Kiedy próbuję utworzyć interfejs dla określonego programu, zwykle staram się unikać zgłaszania wyjątków, które zależą od niepoprawnych danych wejściowych.
Tak więc często zdarza się, że myślałem o takim kodzie (ten przykład jest tylko przykładem, nie przejmuj się funkcją, którą wykonuje, przykład w Javie):
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, powiedzmy, że evenSize
tak naprawdę pochodzi od danych wejściowych użytkownika. Nie jestem więc pewien, czy tak jest. Ale nie chcę wywoływać tej metody z możliwością zgłoszenia wyjątku. Dlatego wykonuję następującą funkcję:
public static boolean isEven(int evenSize) {
return evenSize % 2 == 0;
}
ale teraz mam dwie kontrole, które wykonują tę samą weryfikację danych wejściowych: wyrażenie w if
instrukcji i jawne sprawdzenie isEven
. Duplikat kodu, niezbyt fajny, więc przeróbmy:
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, to rozwiązało, ale teraz dochodzimy do następującej sytuacji:
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)
teraz mamy nadmiarowe sprawdzenie: już wiemy, że wartość jest parzysta, ale padToEvenWithIsEven
nadal wykonuje sprawdzenie parametru, które zawsze zwróci true, ponieważ już wywołaliśmy tę funkcję.
Teraz isEven
oczywiście nie stanowi to problemu, ale jeśli sprawdzanie parametrów jest bardziej kłopotliwe, może to wiązać się z nadmiernymi kosztami. Poza tym wykonywanie zbędnego połączenia po prostu nie wydaje się właściwe.
Czasami możemy obejść ten problem, wprowadzając „typ zatwierdzony” lub tworząc funkcję, w której ten problem nie może wystąpić:
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();
}
ale wymaga to mądrego myślenia i dość dużego refaktora.
Czy istnieje (bardziej) ogólny sposób, w jaki możemy uniknąć zbędnych wywołań isEven
i sprawdzania podwójnych parametrów? Chciałbym, aby rozwiązanie nie wywoływało padToEven
nieprawidłowego parametru, wyzwalając wyjątek.
Bez wyjątków nie mam na myśli programowania bez wyjątków, to znaczy, że dane wprowadzone przez użytkownika nie wyzwalają wyjątku zgodnie z projektem, podczas gdy sama funkcja ogólna nadal zawiera sprawdzanie parametrów (choćby w celu ochrony przed błędami programowania).
padToEvenWithIsEven
nie wykonuje się weryfikacji danych wejściowych użytkownika. Wykonuje sprawdzenie poprawności na wejściu, aby zabezpieczyć się przed błędami programowania w kodzie wywołującym. To, jak obszerna musi być ta walidacja, zależy od analizy kosztów / ryzyka, w której koszt kontroli jest porównywany z ryzykiem, że osoba pisząca kod wywołujący przekaże nieprawidłowy parametr.