Використання спроб остаточно (без вилову) проти перерахунку стану enum-state


9

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

Моя дилема по кращій практиці , чи потрібно використовувати TRY / зловити / нарешті повернути перерахування (або Int , який представляє собою значення, 0 для помилки, 1 для ок, 2 для попередження і т.д., в залежності від випадку) , так що відповідь завжди в порядку, чи варто випустити виняток, щоб частина, яка викликає, вирішила це?

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

Наприклад, на веб-сервісі ви завжди хотіли б повернути стан, звичайно, тому будь-який виняток має бути вирішений на місці, але дозвольте сказати всередині функції, яка публікує / отримує деякі дані через http, ви хочете, щоб виняток (наприклад, у випадку 404) просто перейти до того, хто його вистрілив. Якщо цього не зробити, вам доведеться створити спосіб інформування виклику про якість результату (помилка: 404), а також про сам результат.

Хоча можливо спробувати вилучити виняток 404 всередині допоміжної функції, яка отримує / розміщує дані, чи не так? Чи тільки я використовую маленьку команду для позначення станів у програмі (і, звичайно, відповідним чином документую їх), а потім використовую цю інформацію для перевірки правильності (усе нормально / обробка помилок) зовні?

Оновлення: Я очікував фатального / нефатального винятку для основної класифікації, але я не хотів включати це, щоб не заважати відповідям. Дозвольте мені уточнити, про що йдеться: Поводження з викинутими винятками, а не викидання виключень. Який бажаний ефект: виявіть помилку та спробуйте відновити її. Якщо відновлення неможливо, надайте найбільш змістовні відгуки.

Знову ж таки, із прикладом http get / post, питання полягає в тому, чи слід надати новий об’єкт, який описує, що сталося з вихідним абонентом? Якщо цей помічник був у бібліотеці, яку ви використовуєте, чи очікуєте, що він надасть вам код статусу для цієї операції, або ви включите його до блоку спробу лову? Якщо ви розробляєте його, чи надасте ви код статусу чи викинете виняток і дозволите верхньому рівню замість цього перевести його у код / ​​повідомлення стану?

Конспект: Як ви вибрали, якщо фрагмент коду замість створення винятку повертає код статусу разом з результатами, які він може отримати?


1
Це не стосується помилки, яка змінює форму обробки помилок, що використовується. У випадку 404 ви можете пропустити це через те, що ви не можете впоратися з цим.
кам’яний метал

"int, що представляє значення, 0 для помилки, 1 для нормально, 2 для попередження тощо". Скажіть, що це був приклад без манжети! Використовувати 0, щоб означати ОК, - це точно визначено стандартом ...
anaximander

Відповіді:


15

Винятки слід використовувати для виняткових умов. Якщо викинути виняток, в основному робиться твердження: "Я не можу впоратися з цим умовою тут; чи може хтось вище на стеці викликів схопити це за мене і впоратися з ним?"

Повернення значення може бути кращим, якщо зрозуміло, що абонент візьме це значення і зробить з ним щось значуще. Це особливо справедливо, якщо викид винятку має наслідки для продуктивності, тобто він може відбуватися в тісному циклі. Накидання винятку займає набагато більше часу, ніж повернення значення (принаймні на два порядки).

Винятки ніколи не слід використовувати для реалізації логіки програми. Іншими словами, не кидайте виняток, щоб щось зробити; киньте виняток, щоб сказати, що цього зробити не вдалося.


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

Я бачу вашу редакцію, але це не змінює мою відповідь. Перетворіть виняток у код помилки, якщо це має значення для абонента. Якщо це не так, обробіть виняток; нехай піднімається вгору по стопі; або зловити, зробити щось із цим (наприклад, записати його в журнал чи щось інше) та знову закинути.
Роберт Харві

1
+1: для розумного та зваженого пояснення. Виняток має бути використаний для вирішення виняткових випадків. В іншому випадку функція або метод повинні повернути значуще значення (enum або тип опції), і абонент повинен правильно поводитися з цим. Можливо, можна згадати і третю альтернативу, популярну у функціональному програмуванні, тобто продовження.
Джорджіо

4

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

Наприклад, System.IO.File.OpenRead () передасть файл FileNotFoundException, якщо наданий файл не існує, однак він також забезпечує метод .Exists (), який повертає булеве значення, вказуючи, чи є файл, який ви повинні зателефонувати раніше викликати OpenRead (), щоб уникнути несподіваних винятків.

Щоб відповісти на частину питання "коли я маю справу з винятком", я б сказав, де б ви насправді могли щось зробити. Якщо ваш метод не може вирішити виняток, викинутий методом, який він викликає, не вловлюйте його. Нехай він піднімається вище вгору по ланцюгу дзвінків до чогось, що може з цим впоратися. У деяких випадках це може бути лише реєстратор, який слухає Application.UnhandledException.


Багато людей, особливо програмісти python , віддають перевагу EAFP, тобто "Простіше просити пробачення, ніж отримати дозвіл"
Марк Бут

1
+1 для коментаря щодо уникнення винятків, як у .Exists ().
codingoutloud

+1 Це все-таки хороша порада. У коді, який я пишу / керую, Виняток - "Винятковий", 9/10 разів Виняток призначений для розробника, щоб він бачив, він говорить, що так, вам слід дефенсівлі програмування! В інший раз - це те, з чим ми не можемо мати справу, і ми реєструємо це та виходимо якнайкраще. Консистенція важлива, наприклад, за домовленістю, як правило, у нас є справжній помилковий відгук та внутрішні повідомлення для стандартного тарифу / обробки. Сторонні api, які, здається, кидають винятки з усього, можна обробляти за викликом та повертати за допомогою стандартного узгодженого процесу.
Гевін Хоуден

3

використовуйте спробувати / catch / нарешті, щоб повернути перерахунок (або int, що представляє значення, 0 для помилки, 1 для помилки, 2 для попередження тощо, залежно від випадку), щоб відповідь завжди була в порядку,

Це жахлива конструкція. Не маскуйте виняток, переводячи на числовий код. Залиште це як правильний, однозначний виняток.

чи варто випустити виняток, щоб частина, яка викликає, вирішила це?

Ось для чого є винятки.

розглядаються якомога ближче до місця, де воно підняте

Це зовсім не універсальна правда. Це гарна ідея кілька разів. В інших випадках це не так корисно.


Це не страшний дизайн. Майкрософт реалізує його в багатьох місцях, а саме на постачальнику стандартних послуг asp.NET. Крім цього, я не бачу, як ця відповідь щось сприяє розмові
Mihalis Bagos

@MihalisBagos: Все, що я можу зробити, це припустити, що підхід Microsoft не застосовується кожною мовою програмування. У мовах без винятків повернення значення є важливим. C - найпомітніший приклад. Мови за винятками повернення "значень коду" для вказівки на помилки - жахлива конструкція. Це призводить до (іноді) громіздких ifвисловлювань замість (іноді) більш простого оброблення винятків. Відповідь («Як вибрати…») проста. Не варто . Я думаю, що сказане це додає до розмови. Однак ви можете ігнорувати цю відповідь.
С.Лотт

Я не кажу, що ваша думка не враховує, але я кажу, що ваша думка не розвинена. З цього коментаря я вважаю, що міркування полягають у тому, що де ми можемо використовувати винятки, ми повинні, лише тому, що можемо? Я не вважаю код статусу маскуючим, а не категоризацією / організацією випадків потоку коду
Mihalis Bagos

1
Виняток вже є категоризацією / організацією. Я не бачу значення в заміні недвозначного винятку із зворотним значенням, яке легко можна сплутати з "нормальним" або "невиключним" значенням повернення. Повернені значення завжди повинні бути не винятковими. Моя думка не дуже розвинена: це дуже просто. Не перетворюйте виняток у числовий код. Це вже був ідеально хороший об'єкт даних, який обслуговує всі ідеально хороші випадки використання, які ми можемо собі уявити.
S.Lott

3

Я згоден із С.Лоттом . Ловити виняток якомога ближче до джерела, може бути хорошою ідеєю або поганою ідеєю залежно від ситуації. Ключ до поводження з винятками полягає в тому, щоб зловити їх лише тоді, коли ви можете щось з цим зробити. Ловля їх і повернення числового значення функції виклику, як правило, погана конструкція. Ви просто хочете дозволити їм плисти вгору, поки ви не зможете відновитися.

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

Насправді немає жорсткого і швидкого правила, коли і як налаштувати обробку виключень, окрім як залишити їх у спокої, поки ви не дізнаєтеся, що з ними робити.


1

Виняток становлять красиві речі. Вони дозволяють скласти чіткий опис проблеми часу виконання, не вдаючись до зайвої неоднозначності. Винятки можуть бути введені, субтильовані та можуть оброблятися за типом. Їх можна передавати для обробки в іншому місці, і якщо з ними не можна звертатися, їх можна буде повторно підняти для вирішення на більш високому рівні вашої програми. Вони також автоматично повернуться з вашого методу, не потрібно застосовувати безліч шаленої логіки для боротьби з прихованими кодами помилок.

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

Управління кодами помилок може бути дуже важким. Зазвичай у вас є багато непотрібного дублювання коду та / або безладна логіка для вирішення станів помилок. Бути користувачем і зіткнутися з кодом помилки ще гірше, оскільки сам код не матиме значущого значення і не надасть користувачеві контексту для помилки. Виняток, з іншого боку, може повідомити користувачеві щось корисне, як-от "Ви забули ввести значення" або "Ви ввели недійсне значення, ось допустимий діапазон, який ви можете використовувати ..." або "Я не знайте, що сталося, зв’яжіться з технічною підтримкою та скажіть їм, що я щойно зазнав аварії, і дайте їм наступний слід стека ... ".

Отже, моє запитання до ОП: чому на Землі ви НЕ хочете використовувати винятки щодо повернення кодів помилок?


0

Усі хороші відповіді. Я також хотів би додати, що повернення коду помилки замість викидання виключення може ускладнити код абонента. Скажімо, метод A викликає метод B викликає метод C і C, виникає помилка. Якщо C повертає код помилки, тепер B потрібно мати логіку, щоб визначити, чи може він обробляти цей код помилки. Якщо він не може, то потрібно повернути його до A. Якщо A не вдається виправити помилку, то що робити? Викинути виняток? У цьому прикладі код набагато чистіший, якщо C просто викидає виняток, B не виключає виняток, тому він автоматично перериває без будь-якого додаткового коду, необхідного для цього, а A може ловити певні типи винятків, дозволяючи іншим продовжувати дзвінок стек.

Це враховує хороше правило для кодування:

Рядки коду - як золоті кулі. Ви хочете використати якомога менше, щоб виконати роботу.


0

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

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

Наприклад

procedure Validate(var R:TValidationRecord);
begin
  if Field1 is not valid then
  begin
    R.Field1ErrorCode=SomeErrorCode;
    ErrorFlag := True; 
  end; 
  if Field2 is not valid then
  begin
    R.Field2ErrorCode=SomeErrorCode;
    ErrorFlag := True; 
  end;
  if Field3 is not valid then
  begin
    R.Field3ErrorCode=SomeErrorCode;
    ErrorFlag := True; 
  end;

  if ErrorFlag then
    ThrowException
end;

Якщо покладатися лише на булеві, розробник, який використовує мою функцію, повинен враховувати це:

if not Validate() then
  DoNotContinue();

але він може забути і тільки дзвонити Validate()(я знаю, що не повинен, але, можливо, може).

Отже, у наведеному вище коді я отримав дві переваги:

  1. Лише один виняток у функції перевірки.
  2. Виняток, навіть неприхований, зупинить виконання та з’явиться в тестовий час

0

Тут немає однієї відповіді - начебто немає одного виду HttpException.

Має сенс, що основні бібліотеки HTTP кидають виняток, коли вони отримують відповідь 4xx або 5xx; востаннє я дивився на характеристики HTTP, це були помилки.

Щодо викидання цього винятку - або його згортання та повторного викидання - я думаю, що це справді питання використання. Наприклад, якщо ви пишете обгортку, щоб захопити деякі дані з API і піддавати їх додаткам, ви могли б вирішити, що семантично запит на неіснуючий ресурс, який повертає HTTP 404, має більше сенсу схопити це і повернути нуль. З іншого боку, помилка 406 (неприйнятна), можливо, варто помилитися з помилкою, оскільки це означає, що щось змінилося, і додаток повинен вийти з ладу, горіти і кричати про допомогу.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.