Гарне використання пробних фільмів?


15

Мені завжди доводиться боротися з цим ... намагаючись знайти правильний баланс між спробою / ловом і кодом, що не стає цим нецензурним безладдям вкладок, дужок та винятків, що повертаються назад до стека викликів, як гаряча картопля. Наприклад, у мене є програма, яку я зараз розробляю, яка використовує SQLite. У мене є інтерфейс Бази даних, який абстрагує дзвінки SQLite, і Модель, яка приймає речі для входу / виходу з бази даних ... Отже, якщо / коли виникає виняток SQLite, він повинен перейти до Моделі (хто його назвав ), хто повинен передати це тому, хто назвав AddRecord / DeleteRecord / що завгодно ...  

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

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


Яка мова програмування?
Апалала

2
C # на даний момент, але я намагаюся взагалі думати.
пробір

C # не змушує декларувати викинуті винятки, що полегшує обробку винятків там, де це розумно, і уникає того, щоб програмісти спокусилися їх зловити, фактично не обробляючи їх. Андер Хейлсберг, дизайнер компанії C #, робить справу проти перевірених винятків у цій статті artima.com/intv/handcuffs.html
Апалала,

Відповіді:


14

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

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

Layer      Handles Exception
----------------------------
UI         DataNotFoundException
Model      DatabaseRetrievalException
DAO        SQLException

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


Я вніс декілька змін, погано сформулював оригінал Q .. моє запитання стосується того, як зберегти чистий код під час роботи зі всіма спробами і ловом і чим іншим. Починає відчувати себе дуже безладним, коли спробують ловити блоки всюди ...
trycatch

1
@Ed чи не турбує вас, що ваш інтерфейс користувача або модель ловить "SQLException"? Для мене це не дуже актуально. У кожного свої, я думаю.
Ніколь

1
@Ed, це може бути на зразок нормально на мовах, які не мають перевірених винятків, але в мовах із перевіреними винятками дуже неприємно мати throws SQLExceptionметод, який не означає, що SQL навіть задіяний. А що станеться, якщо ви вирішите, що деякі операції мають перейти до магазину файлів? Тепер ви повинні заявити throws SQLException, IOExceptionі т. Д. Це вийде з рук.
Майк Даніельс

1
@Ed для кінцевого користувача SQLException не означає дуже багато, тим більше, якщо ваша система може підтримувати кілька типів стійкості, крім реляційних баз даних. Як програміст gui я скоріше повинен мати справу з DataNotFoundException, ніж цілий набір винятків низького рівня. Дуже часто виняток із бібліотеки низького рівня є лише частиною звичайного життя вгорі, в цьому випадку з ними набагато простіше впоратися, коли рівень їх абстрагування відповідає рівню програми.
ньютопський

1
@Newtopian - Я ніколи не казав, щоб представити необроблений Виняток кінцевому користувачеві. Кінцевим користувачам достатньо простого повідомлення «Програма перестала працювати» під час реєстрації Винятку десь корисно для підтримки. У режимі налагодження корисно відобразити Виняток. Ви не повинні дбати про всі можливі винятки, які API може викинути, якщо у вас немає конкретного обробника для таких винятків; просто дозвольте вашому уловлювача винятків виправити всі проблеми.
Ед Джеймс

9

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

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

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

У вашому прикладі навряд чи верхні шари можуть зробити що-небудь щодо виключення SQLite (якщо вони є, то це все так поєднано з SQLite, що ви не зможете переключити рівень даних на щось інше). Існує декілька передбачуваних причин, коли такі речі, як Додати / Видалити / Оновити, не вдасться, і деякі з них (несумісні зміни в одночасних транзакціях) неможливо відновити навіть у шарі даних / постійності (порушення правил цілісності, fi). Шар стійкості повинен переводити винятки на щось значуще в умовах модельного шару, щоб верхні шари могли вирішити, чи потрібно повторно намагатись або вийти з ладу.


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

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

1
цей матеріал буде простішим для розуміння на прикладах коду ..
Клацніть Upvote

Це, мабуть, питання, яке краще підходило для стартового потоку з самого початку.
Апалала

5

Як правило, ви повинні ловити лише конкретні винятки (наприклад, IOException), і лише якщо у вас є щось конкретне, щойно ви зробили виняток.

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

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

Обгортання винятків корисно, коли вам потрібно перенести виняток у розподілену систему, і у клієнта немає визначення помилки на сервері.


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

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

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

@Newtopian: "Виключення" винятків та "Переписування" зменшують витік абстракцій. Вони все ще належним чином киплять. "Якщо я не маю уявлення, що на мене йде, як я можу реагувати! Я занадто часто використовую набагато ширші сітки для вилову". Це те, що ми пропонуємо вам перестати робити. Вам не потрібно все ловити. 80% часу правильне - нічого не ловити. У 20% часу є змістовна відповідь.
S.Lott

1
@Newtopian, я думаю, що нам потрібно розрізняти винятки, які зазвичай кидаються об'єктом і, таким чином, повинні бути завернені, та винятки, які виникають через помилки в коді об'єкта і не повинні перетворюватися.
Вінстон Еверт

4

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

  1. ArrayIndexError - піднімається, коли користувач намагається вискочити з порожнього стека
  2. NullPtrException - піднімається через помилку в реалізації, що викликає спробу посилання на нульову посилання

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

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

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

Щоб повернути це до вашого прикладу SQLException: чому ви отримуєте SQLExceptions? Одна з причин - ви передаєте недійсні запити. Однак якщо ваш рівень доступу до даних генерує погані запити, його помилка. Він не повинен намагатися перетворити свою зламаність у виняток DataAccessFailure.

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

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

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