Оборачивается ли все в блоки try / catch защитным программированием? - PullRequest
28 голосов
/ 02 декабря 2008

Я программирую последние 3 года. Когда я программирую, я использую для обработки всех известных исключений и изящно предупреждаю пользователя. Недавно я видел некоторый код, в котором почти все методы заключены в блоки try / catch. Автор говорит, что это часть защитного программирования. Интересно, это действительно защитное программирование? Вы рекомендуете помещать весь свой код в блоки try?

Ответы [ 12 ]

56 голосов
/ 02 декабря 2008

Мое основное правило: если вы не можете исправить проблему, которая вызвала исключение, не перехватывайте его, дайте ему всплыть до уровня, когда с ним можно разобраться.

По моему опыту, 95% всех блоков catch либо просто игнорируют исключение (catch {}), либо просто регистрируют ошибку и перебрасывают исключение. Последнее может показаться правильным решением, но на практике, когда это делается на каждом уровне, вы просто заканчиваете тем, что ваш журнал загроможден пятью копиями одного и того же сообщения об ошибке. Обычно эти приложения имеют «игнорировать улов» на самом верхнем уровне (так как «у нас есть попытки / ловить на всех нижних уровнях»), что приводит к очень медленному приложению с большим количеством пропущенных исключений и журналу ошибок, который слишком длинный любой желающий просмотреть это.

19 голосов
/ 02 декабря 2008

Широкое использование Try ... Catch - не защитное программирование, оно просто пригвоздит труп в вертикальном положении .

Попробуйте ... Наконец-то можно широко использовать для восстановления перед лицом неожиданных исключений. Только если вы ожидаете исключение и теперь, как с ним бороться, используйте вместо этого Try..Catch.

Иногда я вижу Try..Catch System.Exception, где блок catch просто регистрирует исключение и повторно выдает его. При таком подходе есть как минимум 3 проблемы:

  • Ретроу допускает необработанное исключение, и поэтому программа должна завершиться, поскольку она находится в неизвестном состоянии. Но catch запускает блоки finally под блоком Catch. В неопределенной ситуации код в этих блоках "В конце" может усугубить проблему.
  • Код в этих блоках Final изменит состояние программы. Таким образом, любое ведение журнала не будет отражать фактическое состояние программы, когда было сгенерировано исключение. И расследование будет сложнее, потому что состояние изменилось.
  • Это дает вам жалкий опыт отладки, потому что отладчик останавливается на повторном броске, а не на исходном броске.
14 голосов
/ 02 декабря 2008

Нет, это не «защитное программирование». Ваш коллега пытается оправдать свою плохую привычку, используя модное слово для хорошей привычки.

То, что он делает, должно называться «подметание под ковром». Это похоже на (void) -ing возвращаемое значение состояния ошибки из вызовов метода.

11 голосов
/ 02 декабря 2008

Термин «защитное программирование» означает написание кода таким образом, чтобы он мог восстанавливаться после возникновения ошибок или чтобы он вообще избегал ошибок. Например:

private String name;

public void setName(String name) {
}

Как вы обрабатываете имя == ноль? Вы бросаете исключение или принимаете его? Если не имеет смысла иметь объект без имени, вы должны выбросить исключение. А как насчет имени == ""?

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

Другой пример:

public boolean isXXX (String s) {
}

Здесь защитная стратегия часто состоит в том, чтобы возвращать false, когда s == null (избегайте NPE, когда можете).

Или:

public String getName() {
}

Защитный программист может вернуть "", если name == null, чтобы избежать NPE в вызывающем коде.

9 голосов
/ 02 декабря 2008

Если вы собираетесь обрабатывать случайные исключения, обрабатывайте их только в одном месте - самом верху приложения, для целей:

  • представляет дружественное сообщение пользователю и
  • сохранение диагностики.

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

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

В общем, если ... остальное намного лучше, чем попробовать ... поймать.

8 голосов
/ 02 декабря 2008

Поймать случайные исключения плохо. Что тогда?

  • Игнорировать их? Отлично. Дайте мне знать, как это работает для них.
  • Войти их и продолжать работать? Думаю, нет.
  • Бросить другое исключение как часть сбоя? Удачи в отладке этого.

Хорошо ловить исключения, для которых вы можете сделать что-то значимое. Эти случаи легко идентифицировать и поддерживать.

6 голосов
/ 02 декабря 2008

Могу ли я просто сказать в качестве отступления, что каждый раз, когда один из моих коллег пишет сигнатуру метода с «throws Exception» вместо перечисления типов исключений, которые действительно выдает метод, я хочу перейти и снять их в голова? Проблема в том, что через некоторое время у вас есть 14 уровней вызовов, каждый из которых говорит «бросает исключение», поэтому рефакторинг, заставляющий их объявлять, что они действительно бросают, является основным упражнением.

5 голосов
/ 02 декабря 2008

Существует такая вещь, как «слишком большая» обработка, и перехват всех исключений как бы побеждает точку. Специально для C ++ оператор catch (...) перехватывает все исключения, но вы не можете обработать содержимое этого исключения, потому что вы не знаете тип исключения (и это может быть что угодно).

Вам следует перехватывать исключения, которые вы можете обработать полностью или частично, перебрасывая частичные исключения. Вы не должны перехватывать любые исключения, которые вы не можете обработать, потому что это просто запутывает ошибки, которые могут (или, скорее, будут) кусать вас позже.

4 голосов
/ 02 декабря 2008

В C ++ одной из причин написания большого количества блоков try / catch является получение трассировки стека того, где было сгенерировано исключение. То, что вы делаете, это пишите try / catch везде и (при условии, что вы не в нужном месте, чтобы разобраться с исключением), получите в журнале catch некоторую информацию о трассировке, а затем перезапустите исключение. Таким образом, если исключение пузырится до конца и приводит к завершению программы, у вас будет полная трассировка стека того, где все начиналось не так (если вы этого не сделаете, то необработанное исключение C ++ будет якобы размотал стек и уничтожил любую возможность, чтобы вы выяснили, откуда он взялся).

Я бы предположил, что на любом языке с лучшей обработкой исключений (т. Е. Неперехваченные исключения сообщают вам, откуда они взялись) вы бы хотели перехватывать исключения, только если могли бы что-то с ними сделать. В противном случае вы просто затрудняете чтение вашей программы.

4 голосов
/ 02 декабря 2008

Я бы рекомендовал против этой практики. Поместить код в блоки try-catch, когда вы знаете типы исключений, которые могут быть выданы, - это одно. Это позволяет вам, как вы заявили, корректно восстановить и / или предупредить пользователя об ошибке. Однако, помещая весь свой код в такие блоки, где вы не знаете, в чем может заключаться ошибка, используется исключение для управления потоком программы, что является большим нет-нет.

Если вы пишете хорошо структурированный код, вы будете знать о каждом исключении, которое может произойти, и сможете отловить его специально. Если вы не знаете, как генерировать конкретное исключение, то не поймайте его, на всякий случай. Когда это происходит, вы можете выяснить исключение, что вызвало его, а затем поймать его.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...