Когда я пытаюсь создать интерфейс для конкретной программы, я обычно стараюсь избегать исключений, которые зависят от неподтвержденного ввода.
Поэтому часто случается так, что я думаю о таком фрагменте кода (это просто пример ради примера, не обращайте внимания на функцию, которую он выполняет, пример в 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();
}
Хорошо, так сказать, что evenSize
на самом деле происходит от ввода пользователя. Так что я не уверен, что это даже. Но я не хочу вызывать этот метод с вероятностью возникновения исключения. Поэтому я выполняю следующую функцию:
public static boolean isEven(int evenSize) {
return evenSize % 2 == 0;
}
но теперь у меня есть две проверки, которые выполняют одинаковую проверку входных данных: выражение в if
операторе и явная регистрация isEven
. Дублирующий код, не очень хороший, так что давайте сделаем рефакторинг:
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();
}
Хорошо, это решило проблему, но теперь мы попадаем в следующую ситуацию:
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)
Теперь у нас есть избыточная проверка: мы уже знаем, что значение четное, но padToEvenWithIsEven
все еще выполняет проверку параметров, которая всегда будет возвращать true, как мы уже вызывали эту функцию.
Конечно, сейчас isEven
проблем не возникает, но если проверка параметров более громоздкая, то это может повлечь за собой слишком большие затраты. Кроме того, выполнение избыточного вызова просто не кажется правильным.
Иногда мы можем обойти это, введя «проверенный тип» или создав функцию, где эта проблема не может возникнуть:
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();
}
но это требует умного мышления и довольно большого рефакторинга.
Есть ли (более) общий способ, которым мы можем избежать избыточных вызовов isEven
и выполнять двойную проверку параметров? Я бы хотел, чтобы решение не вызывало padToEven
неверный параметр, вызывая исключение.
Без исключений я не имею в виду программирование без исключений, я имею в виду, что пользовательский ввод не вызывает исключение по конструкции, в то время как сама универсальная функция все еще содержит проверку параметров (если только для защиты от ошибок программирования).
padToEvenWithIsEven
не выполняет проверку ввода пользователя. Он выполняет проверку достоверности своего ввода, чтобы защитить себя от ошибок программирования в вызывающем коде. Насколько обширной должна быть эта проверка, зависит от анализа затрат / рисков, где вы сравниваете стоимость проверки с риском того, что лицо, пишущее код вызова, передает неверный параметр.