Функція повертає true / false порівняно з void при успіху та викиданні винятку при відмові


22

Я будую API, функцію, яка завантажує файл. Ця функція не поверне нічого / недійсним, якщо файл завантажено правильно та викине виняток, коли виникла якась проблема.

Чому виняток, а не просто хибний? Тому що всередині винятку я можу вказати причину збою (відсутність з'єднання, відсутні файлове ім'я, неправильний пароль, відсутність опису файлу тощо). Я хотів створити спеціальний виняток (з деякими перерахунками, щоб допомогти користувачеві API обробити всі помилки).

Це хороша практика чи краще повернути об’єкт (з булевим усередині, необов’язковим повідомленням про помилку та перерахуванням помилок)?



59
Справжні та помилкові добре йдуть разом. Пустота та виняток добре поєднуються. Правда та виняток йдуть разом, як коти та податки. Колись я, можливо, буду читати ваш код. Будь ласка, не робіть цього мені.
candied_orange

4
Мені дуже подобаються шаблони, коли повертається об'єкт, який інкапсулює успіх чи невдачу. Наприклад, іржа , Result<T, E>де Tрезультат в разі успіху , і Eце помилка , якщо безуспішними. Закидання винятку (може бути - не настільки впевнений у Java) може бути дорогим, оскільки воно передбачає розмотування стека викликів, але створення об’єкта «Виняток» - дешево. Очевидно, дотримуйтесь встановлених моделей мови, яку ви використовуєте, але не поєднуйте булеві та винятки, як це.
Ден комора

4
Будьте обережні тут. Можливо, ви будете спрямовані вниз у кролячу нору. Коли ваша програма здатна вирішити проблему, зазвичай простіше просто виявити та вирішити її, перевіривши передумови. Ви рідко спіймаєте виняток, вивчіть його дані в коді та повторіть спробу після усунення проблеми. Отже, рідко цінно мати дуже багато типів виключень. Якщо ви намагаєтеся зареєструвати проблеми, це має набагато більше сенсу, але не сподівайтеся написати купу блоків із значним кодом у них.
jpmc26

4
@DanPantry У Java стек викликів перевіряється під час створення винятку, а не під час його перекидання. fillInStackTrace();називається в супер конструкторі, в класіThrowable
Саймон Форсберг

Відповіді:


35

Закидання винятку - це просто додатковий спосіб повернути значення методу. Абонент може перевірити значення повернення так само легко, як і зловити виняток, і перевірити це. Тому, вирішуючи між собою throwта returnвимагаючи інших критеріїв.

Закидання винятків часто слід уникати, якщо це ставить під загрозу ефективність вашої програми (побудова об'єкта виключення та розмотування стека виклику - це набагато більше роботи для комп'ютера, ніж просто натискання на нього значення). Але якщо мета вашого методу - завантажити файл, тоді вузьким місцем завжди буде введення / виведення мережевої та файлової системи, тому оптимізувати метод повернення безглуздо.

Це також погана ідея викидати винятки для того, що має бути простим контрольним потоком (наприклад, коли пошук досягає успіху, знаходячи його значення), оскільки це порушує очікування користувачів API. Але метод, який не виконує своєї мети, є винятковим випадком (або, принаймні, таким повинен бути), тому я не бачу причин не кидати виняток. І якщо ви це зробите, ви можете так само зробити його спеціальним, більш інформативним винятком (але це гарна ідея зробити його підкласом стандартного, більш загального винятку, як IOException).


7
Якщо очікуваний результат для запиту на завантаження файлу не вдасться, я був би здивований винятком, кинутим мені в обличчя (особливо якщо в підписі методу є повернене значення).
hoffmale

1
Зауважте, що причина його поширення є стандартним винятком у тому, що багато (можливо, навіть більшість) абонентів не напишуть код, щоб спробувати усунути проблему. Як результат, більшість абонентів захочуть просте, "зловити цю велику групу винятків", без необхідності чітко їх перераховувати.
jpmc26

4
@gbjbaanb Ось чому слід використовувати винятки, коли справді існує якась незлагоджена ситуація. Коли ви намагаєтесь відкрити файл, і файл не може бути прочитаний, це хороший випадок для виключення. Перевіряючи наявність файлу, потрібно використовувати булевий формат, оскільки очікується, що файл може не бути там.
Малькольм

21
Кіліан каже, що в рекурсивному слогані "винятки є для виняткових ситуацій" виняткові ситуації, коли функція не може виконати своє призначення. Якщо функція повинна читати файл, а файл не може бути прочитаний , це виключно . Якщо функція передбачає, чи існує файл чи ні (неважливо, потенційний TOCTOU), то файл, який не існує , не є винятковим, оскільки функція все ще може робити те, що має робити. Якщо функція повинна стверджувати, що файл існує, то він не існує, є винятковим, оскільки саме це означає "затвердження".
Стів Джессоп

10
Зауважте, що єдиною різницею між функцією, яка повідомляє про те, чи існує файл, і функцією, яка стверджує, що файл існує, є виняток у випадку, якщо файл не існує. Люди іноді говорять так, ніби це властивість умови помилки, незалежно від того, чи це "винятковий". Це не властивість умови помилки, це комбіноване властивість умови помилки та цілі функції, в якій вона відбувається. Інакше ми ніколи не будемо ловити винятки.
Стів Джессоп

31

Немає абсолютно ніяких причин для повернення trueуспіху, якщо ви не повернетеся falseдо невдачі. Як повинен виглядати код клієнта?

if (result = tryMyAPICall()) {
    // business logic
}
else {
    // this will *never* happen anyways
}

У такому випадку абоненту в будь-якому випадку потрібен блок пробування, але тоді він може краще написати:

try {
    result = tryMyAPICall();
    // business logic
    // will only reach this line when no exception
    // no reason to use an if-condition
} catch (SomeException se) { }

Тож trueповернене значення абсолютно не має значення для абонента. Тому просто дотримуйтесь методу void.


Загалом, є три способи проектування несправних режимів.

  1. Повернути справжнє / хибне
  2. Використовуйте void, киньте (перевірено) виняток
  3. повернути проміжний результат результату .

Повернення true/false

Це використовується в деяких старих, в основному, API стилів. Мінуси - це очевидність, ви поняття не маєте, що пішло не так. PHP робить це досить часто, приводячи до такого коду:

if (xyz_parse($data) === FALSE)
   $error = xyz_last_error();

У багатопотокових контекстах це ще гірше.

Киньте (перевірені) винятки

Це хороший спосіб зробити це. У певні моменти можна очікувати невдачі. Java робить це за допомогою розеток. Основне припущення полягає в тому, що виклик повинен бути успішним, але всі знають, що певні операції можуть бути невдалими. Розеткові з'єднання є серед них. Тож абонент змушений впоратися з відмовою. його приємний дизайн, оскільки він гарантує, що абонент насправді справляється з відмовою, і надає абоненту елегантний спосіб подолати несправність.

Повернути об'єкт результату

Це ще один приємний спосіб впоратися з цим. Його часто використовують для розбору або просто речей, які потрібно перевірити.

ValidationResult result = parser.validate(data);
if (result.isValid())
    // business logic
else
    error = result.getvalidationError();

Приємна, чиста логіка і для абонента.

Існує трохи дебатів, коли використовувати другий випадок, а коли використовувати третій. Деякі люди вважають, що винятки повинні бути винятковими і що ви не повинні розробляти з можливістю винятків на увазі, і майже завжди будуть використовувати треті варіанти. Цей штраф. Але ми перевірили винятки на Java, тому я не бачу причин не використовувати їх. Я використовую перевірені очікування, коли основне припущення полягає в тому, що виклик повинен перевищувати (як, наприклад, використання сокета), але помилка можлива, і я використовую третій варіант, коли його дуже незрозуміло, якщо виклик повинен перевищити (як перевірка даних). Але на це є різні думки.

У вашому випадку я б пішов з void+ Exception. Ви очікуєте, що завантаження файлу завершиться, а коли воно не відбудеться, його виняткове. Але абонент змушений керувати цим режимом відмови, і ви можете повернути Виняток, який належним чином описує, яка помилка виникла.


5

Це дійсно зводиться до того, чи є відмова винятковою чи очікуваною .

Якщо помилка - це не те, що ви зазвичай очікуєте, то цілком ймовірно, що користувач API просто зателефонує вашому методу без будь-яких спеціальних помилок. Якщо викинути виняток, ви зможете підняти стек до місця, де його помітять.

Якщо з іншого боку помилки зустрічаються звичайно, вам слід оптимізувати розробника, який перевіряє їх, і try-catchпропозиції є дещо громіздкішими, ніж if/elifчи switchсерії.

Завжди розробляйте API у свідомості того, хто ним користується.


1
У моєму випадку, як хтось зазначав, має бути винятковий збій, що користувач API не може завантажити файл.
Accollativo

4

Не повертайте булевих, якщо ви ніколи не збираєтесь повертатися false. Просто зробіть метод voidі документуйте, що він викине виняток ( IOExceptionпідходить) при відмові.

Причиною цього є те, що якщо ви повернете булевий файл, користувач вашого API може зробити висновок, що він / вона може це зробити:

if (fileUpload(file) == false) {
    // Handle failure.
}

Це, звичайно, не вийде; тобто існує невідповідність між договором вашого методу та його поведінкою. Якщо ви кинете перевірений виняток, користувач методу повинен впоратися з відмовою:

try {
    fileUpload(file);
} catch (IOException e) {
    // Handle failure.
}

3

Якщо ваш метод має значення повернення, кидання винятку може здивувати його користувачів. Якщо я бачу, що метод повертає булевий сигнал, я впевнений, що він повернеться true, якщо він буде успішним та false, якщо не вдасться, і я буду структурувати свій код за допомогою пункту if-else. Якщо ваш виняток все одно базуватиметься на enum, ви можете також повернути значення enum замість цього, як у Windows HResults, і зберегти значення enum, коли метод успішний.

Також дратівливим є виняток мета мета, коли це не є завершальним кроком у процедурі. Необхідно написати контрольний потік і відвернути потік управління до блоку вилову - хороший рецепт спагетті, і цього слід уникати.

Якщо ви рухаєтесь уперед із винятками, спробуйте повернути недійсність замість цього, користувачі зрозуміють, що це успішно, якщо не буде викинуто жодних винятків, і жоден не спробує використати пункт if-else для відведення керуючого потоку.


1
Перерахунок був просто ідеєю допомогти користувачеві API обробляти різні типи помилок, не аналізуючи повідомлення про помилку. Тож з вашої точки зору в моєму випадку краще повернути перерахунок і додати перерахунок для "ОК".
Accollativo
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.