Коли використовувати твердження і коли використовувати виняток


121

Більшу частину часу я буду використовувати виняток, щоб перевірити стан у своєму коді, мені цікаво, коли настає відповідний час для використання твердження?

Наприклад,

Group group=null;
try{
    group = service().getGroup("abc");
}catch(Exception e){
    //I dont log error because I know whenever error occur mean group not found
}

if(group !=null)
{
    //do something
}

Чи можете ви вказати, як тут укладається твердження? Чи варто використовувати твердження?

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

Відповіді:


81

Твердження повинні використовуватися для перевірки того, що ніколи не повинно відбуватися, тоді як виняток слід використовувати для перевірки того, що може статися.

Наприклад, функція може ділитися на 0, тому слід використовувати виняток, але твердження може бути використане для перевірки того, що жорсткий диск раптово зникає.

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

Зауважте, що if(group != null)це не твердження, а лише умовне.


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

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

1
@Marius Ваш умовний варіант можна замінити таким твердженням: assert group! = Null
IgorGanapolsky

1
Неприйнятний, тому що приклад жорсткого диска суперечить вашій власній філософії. Жорсткі диски, що "зникають" (з точки зору коду), насправді можуть трапитися в реальності - як би це не було неймовірним. Як каже @IanGoldby, твердження повинні суто залежати від речей, які контролюються вашим кодом.
Vicky Chijwani

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

169

Я не знаю (список може бути неповним і занадто довгим, щоб вмістити коментар), я б сказав:

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

Іншими словами, винятки стосуються надійності вашої програми, а твердження - її коректності.

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


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

Я перекреслював це на своєму сайті: pempek.net/articles/2013/11/16/assertions-or-exceptions
Грегорі Пакош

І якщо вам трапляється шукати користувальницьку бібліотеку для затвердження C ++, я випустив github.com/gpakosz/Assert
Григорій Пакош

26

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

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


Hue hue hue Мені було цікаво, чому мій код не працює в Eclipse, але працює добре в командному рядку.
Стів

15

Як правило:

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

Наступний код вашого запитання - це поганий стиль та потенційно баггі

try {
    group = service().getGroup("abc");
} catch (Exception e) {
    //i dont log error because i know whenever error occur mean group not found
}

Проблема полягає в тому, що ви не знаєте, що виняток означає, що група не була знайдена. Можливо також, що service()виклик кинув виняток, або що він повернувся, nullщо потім викликало a NullPointerException.

Коли ви ловите "очікуваний" виняток, ви повинні ловити лише той виняток, якого ви очікуєте. Захоплюючи java.lang.Exception(і особливо не записуючи його), ви ускладнюєте діагностувати / налагоджувати проблему та потенційно дозволяєте додатку робити більше шкоди.


4

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

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

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


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

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

3

Відповідно до цього документа http://docs.oracle.com/javase/6/docs/technotes/guides/language/assert.html#design-faq-general , "Заява твердження підходить для непублічної передумови, постусловості та інваріанта класу Перевірка загальнодоступних передумов все-таки повинна проводитися за допомогою перевірок усередині методів, які призводять, зокрема, задокументовані винятки, такі як IllegalArgumentException та IllegalStateException. "

Якщо ви хочете дізнатися більше про передумову, постусловість та інваріант класів, перевірте цей документ: http://docs.oracle.com/javase/6/docs/technotes/guides/language/assert.html#usage-conditions . Він також містить приклади використання тверджень.


1

Тестування на null зафіксує лише нулі, що спричиняють проблеми, тоді як спроба / catch, як у вас є, призведе до будь-якої помилки.

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


1

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


1

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

Тож використовуючи твердження на Java

  1. є більш стислим засобом написання блоку умови / кидання
  2. дозволяє включити / вимкнути ці перевірки за допомогою параметрів JVM. Зазвичай я залишаю ці перевірки постійно, якщо вони не впливають на виконання часу або не мають подібного штрафу.

AssertionError - це підклас Error not RuntimeException.
Стівен С

Ага. Звичайно. Я думав про перевірене / не перевірене. Тепер виправлено
Брайан Агнеу

Він пояснює, що таке твердження (з суперечливої ​​точки зору), але не коли саме їх використовувати.
Карл Ріхтер

1

Дивіться розділ 6.1.2 (Затвердження проти іншого коду помилки) документації Sun за наступним посиланням.

http://www.oracle.com/technetwork/articles/javase/javapch06.pdf

Цей документ дає найкращі поради, які я бачив, коли використовувати твердження. Цитування з документа:

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


0

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

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