Як уникнути неприємних винятків?


21

Читання статті Еріка Ліпперта про винятки, безумовно, було відкриття очей щодо того, як я повинен підходити до винятків як як виробника, так і як споживача. Однак я все ще намагаюся визначити керівництво щодо того, як уникнути неприємних винятків.

Конкретно:

  • Припустимо , у вас є метод Save , який може потерпіти невдачу , тому що а) Хто - то змінив запис , перш ніж ви , або б) Значення , яке ви намагаєтеся створити , вже існує . Ці умови слід очікувати і не є винятковими, тому замість того, щоб викидати виняток, ви вирішили створити спробуйте версію свого методу TrySave, яка повертає булеву інформацію, вказуючи, чи збереження вдалося. Але якщо це не вдасться, як споживач дізнається, у чому була проблема? Або краще було б повернути перерахунок із зазначенням результату, на зразок Ok / RecordAlreadyModified / ValueAlreadyExists? З integer.TryParse цієї проблеми не існує, оскільки існує лише одна причина, коли метод може вийти з ладу.
  • Чи справді попередній приклад неприємна ситуація? Або кидати виняток у цьому випадку буде кращим способом? Я знаю, що це робиться в більшості бібліотек та рамок, включаючи структуру Entity.
  • Як ви вирішите, коли створити спробуйте версію свого методу, передбачивши якийсь спосіб попередньо перевірити, чи метод буде працювати чи ні? Зараз я дотримуюся цих рекомендацій:
    • Якщо є шанс на стан гонки, то створіть Спробуйте версію. Це перешкоджає потребі споживача зловити екзогенний виняток. Наприклад, у описаному раніше способі збереження.
    • Якщо метод тестування стану в значній мірі зробив би все, що робить оригінальний метод, тоді створіть Спробуйте версію. Наприклад, integer.TryParse ().
    • У будь-якому іншому випадку створіть метод перевірки стану.

1
Ваш приклад економії, який може не вдатися, насправді не є надзвичайно пристрасним винятком. Це зовсім звичайно і, мабуть, просто має бути винятком.
S.Lott

@ S.Lott: Що ти маєш на увазі з цілком звичайним? Сама ситуація чи кидання у цій ситуації винятку? У всякому разі, я погоджуюся з вами, що це не очевидно, чи справді це неприємна ситуація. Я оновлю питання.
Майк

"Сама ситуація, або викидання виключення в цій ситуації" Обидва.
S.Lott

Відповіді:


24

Припустимо, у вас є метод Зберегти, який може вийти з ладу, оскільки: a) Хтось ще змінив запис перед вами; Ці умови слід очікувати і не є винятковими, тому замість того, щоб викидати виняток, ви вирішили створити спробуйте версію свого методу TrySave, яка повертає булеву назву із зазначенням успіху збереження. Але якщо це не вдасться, як споживач дізнається, у чому була проблема?

Гарне питання.

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

Друге питання, яке мені спадає на думку, таке: чи інформація, яку ви хочете повернути користувачеві, підлягає дії ? Тобто, чи приймуть якесь рішення на основі цієї інформації?

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

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

Припустимо, для аргументу, що абонент дійсно буде вживати різних дій залежно від причини, чому операція не вдалася.

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

Припустимо заради аргументу, що це не є винятковим.

Четверте питання, яке спадає на думку: чи є спосіб надійно виявити погану ситуацію достроково?

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

П'яте питання, яке спадає на думку, - чи є спосіб розробити API, щоб запобігти поганій ситуації?

Наприклад, ви могли зробити так, щоб операція "збереження" вимагала двох кроків. Перший крок: придбайте блокування запису, який змінюється. Ця операція або успішна, або невдача, і тому може повернути булевий стан. Тоді, хто телефонує, може мати політику щодо того, як боротися з невдачею: зачекайте деякий час і спробуйте знову, відмовтеся від будь-якого. Крок другий: як тільки замок придбаний, зробіть збереження та відпустіть замок. Тепер збереження завжди вдається, і тому не потрібно турбуватися про будь-які помилки. Якщо збереження не вдається, це справді винятково.


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

10
@Mike: Файлові системи - хороший приклад використання екзогенних винятків. Вони провалюються рідко, тому невдача є винятковою. Вони виходять з ладу непередбачувано, і з причин, які повністю не перебувають під контролем абонента (немає жодного "блокування", який можна взяти, щоб утримувати кабель Ethernet підключеним), а збої є різноманітними та діючими (тобто помилка, оскільки файл є Не знайдено vs файл знайдено, але у вас немає доступу до запису, вони можуть діяти по-різному.) Все це є причинами, що представляють помилку як виняток.
Ерік Ліпперт

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

@Mike Я не можу говорити за Еріка, але це здається гарним місцем для кодів помилок. Повернення члена енту, можливо.
Матвій

1

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

Загалом, якщо є умова, на яку я думаю, що це дуже вірогідно (наприклад, NoDataReturned, DivideByZero тощо). АБО це дуже легко перевірити (наприклад, порожня колекція або значення NULL), я намагаюся перевірити наявність це достроково і розібратися з цим, перш ніж я коли-небудь дістанусь до того моменту, коли мені доведеться зловити виняток. Я визнаю, що не завжди легко дізнатися про ці умови заздалегідь, іноді вони з'являються лише тоді, коли код проходить ретельне тестування.


0

save()Метод повинен кинути виняток.

Самий верхній шар повинен охоплювати та інформувати користувача , не припиняючи програму, якщо тільки це не програми Unix, як командного рядка, і в цьому випадку це нормально припиняти.

Повернені значення не є хорошим способом керування винятками.

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