Рекомендовать шаблон проектирования / подход к выявлению / терпению / восстановлению после системных ошибок, обработке исключений (например, в Java, C ++, Perl, PHP)

13

Можете ли вы порекомендовать шаблон проектирования / подход к выявлению / переносу / восстановлению после системных ошибок, обработке исключений (Java, C ++, Perl, PHP)?

О некоторых ошибках нужно сообщать.

Некоторые ошибки могут быть обработаны внутри (повторной попыткой или несущественными (могут быть проигнорированы).

Как вы структурируете код, чтобы поймать их?

Но все ошибки должны быть зарегистрированы.

Какие лучшие практики есть?

И для того, чтобы имитировать их, чтобы иметь возможность полностью протестировать компоненты, на которые они влияют?

Общий конкретный вопрос, не относящийся к языку программирования, применим к нескольким современным языкам программирования, но приветствовал бы примеры иллюстраций шаблонов, подходов и философий на Java, C ++, PHP и Perl.

(Также спрашивается в stackoverflow: /programming/7432596/recommend-a-design-pattern-approach-to-exposing-tolerating-recovering-from-system, но я подумал, что это следует спросить и у программистов, потому что Я думаю, что вопросы и ответы программистов охватывают более широкие вопросы программного обеспечения / программирования, в то время как stackoverflow - больше о технической реализации (IMHO).

therobyouknow
источник
2
Для меня ваш вопрос кажется слишком общим и открытым, чтобы на него можно было ответить эффективно. Попробуйте ограничить его некоторыми более конкретными случаями, а также определенными типами приложений / средами (например, приложение GUI / сервер / ...).
Петер Тёрёк
См. Также stackoverflow.com/questions/937285/…
Петер Тёрёк
@ Петер Török, Микера дает хороший ответ, вероятно, примут их.
therobyouknow

Ответы:

16

Fail fast - отличный подход к проектированию, и, возможно, его можно считать шаблоном: http://en.wikipedia.org/wiki/Fail-fast

Я также нашел несколько принципов, которые будут полезны:

  • Рассматривайте исключения как часть интерфейса для каждой функции / модуля - то есть документируйте их и, где необходимо, / если ваш язык поддерживает это, тогда используйте проверенные исключения.
  • Никогда не выдумывайте неудачу - если она не удалась, не пытайтесь продолжить с некоторой попыткой «предположить», как действовать дальше. Например, специальная обработка для нулевых случаев часто является для меня запахом кода: если вашему методу требуется ненулевое значение, он должен немедленно генерировать исключение, если он встречает нулевое значение (либо NullPointerException, либо, в идеале, более описательный IllegalArgumentException).
  • Напишите модульные тесты для исключительных и обычных случаев - иногда такие тесты могут быть сложны в настройке, но это того стоит, если вы хотите быть уверены, что ваша система устойчива к сбоям
  • Журнал в точке, где ошибка была обнаружена и обработана (при условии, что ошибка была достаточно серьезной для регистрации). Причина в том, что это означает, что вы поняли причину и имели подход к обработке ошибки, поэтому вы можете сделать лог-сообщение значимым .....
  • Используйте исключения только для действительно неожиданных условий / сбоев. Если ваша функция «дает сбой» таким образом, который на самом деле ожидается (например, опрос, чтобы увидеть, доступно ли больше ввода, и не обнаружил ни одного), то она должна вернуть нормальный ответ («нет ввода»), а не выдавать исключение
  • Изящный провал (благодарность Стивену Лоу!) - очистите перед прекращением, если это вообще возможно, обычно путем отмены изменений, отката транзакции или освобождения ресурсов в операторе «наконец» или его эквиваленте. В идеале очистка должна происходить на том же уровне, на котором ресурсы выделялись ради ясности и логической последовательности.
  • Если вам приходится терпеть неудачу, громко терпите неудачу - исключение, которое ни одна часть вашего кода не смогла обработать (т.е. перколировать до верхнего уровня, не будучи пойманным + обработанным), должна вызвать немедленный, громкий и видимый сбой, который направляет ваше внимание на него. Я обычно останавливаю программу или задачу и пишу полный отчет об исключениях в System.out.
mikera
источник
1
также изящно терпеть неудачу - очистить перед прекращением, если это вообще возможно
Стивен А. Лоу
+1 @mikera для ясных моментов о том, что рассмотреть. Вероятно, принятый ответ, я оставлю открытым немного дольше, чтобы другие могли внести свой вклад.
therobyouknow
+1 @ Стив А. Лоу - за ориентированный на пользователя дизайн. например, как сохранение файлов, а не оставлять временные файлы без дела. См. Мой вопрос о «гигиене данных» как связанную тему в моем списке вопросов.
therobyouknow
5

Поработав с исключениями в Java и .NET и прочитав много статей о том, как / когда / почему отлавливать исключения, я, наконец, придумал следующие шаги, которые я выполняю в своей голове, когда вижу потенциальное исключение или Исключение я должен поймать (Java) ... даже если это никогда не происходит (вздох ...). И это, кажется, работает, по крайней мере для меня:

  1. Что-нибудь полезное, что я могу сделать с этим исключением (кроме регистрации)? Если ответ «да», напишите код обходного пути, и если обходной путь может вызвать исключения, перейдите к 2:
  2. Оберните исключение вокруг исключения во время выполнения, бросьте его, перейдите к 3.
  3. В классе более высокого уровня, где была инициирована возможная транзакция базы данных / процесса, перехватите исключение, откатите транзакцию, сбросьте исключение.
  4. В классе верхнего уровня (который может быть тем, где была инициирована транзакция), зарегистрируйте исключение с помощью каркаса ведения журнала, такого как slf4j (в сочетании с log4j, например) или log4net . Если возможно, отправьте исключение по электронной почте напрямую в список рассылки, составленный разработчиками приложения.
  5. Если есть GUI, отобразите сообщение об ошибке, указывающее наиболее удобным для пользователя способом, что вызвало проблему; не отображать исключение / трассировку стека, пользователь не заботится и не должен знать, что это исключение NullPointerException.

Я также должен добавить шаг 0 , где я намеренно выбрасываю то, что я называю «бизнес-исключением» (новое исключение, которое я создаю, расширяя класс «Исключение»), когда некоторая сложная обработка не может быть выполнена из-за ошибок данных, НО это Известно, что это происходит, поскольку они были определены как исключительные случаи во время анализа.

За исключением части регистрации, я полностью согласен с пунктами, написанными "mikera"; Я просто добавлю, что исключение должно быть зарегистрировано один раз только .

Кроме того, шаги, которые я перечислил, могут отличаться, если вы пишете API / Framework . Там, создание хорошо продуманных исключений обязательно, чтобы помочь разработчикам понять их ошибки.

Что касается тестирования исключений, используя фиктивные объекты, вы должны иметь возможность тестировать практически все, будь то исключения или нет, при условии, что ваши классы соблюдают лучшие практики «один класс - для одной вещи». Я также лично отмечаю, что наиболее важные, но скрытые методы помечены как «защищенные», а не как «частные», чтобы я мог проверить их без особых хлопот. Кроме того, тестирование исключений является простым, просто спровоцируйте исключение и «ожидайте», что возникнет исключение, перехватывая его. Если вы не получите исключение, значит, у вас ошибка в модульном тесте.

Jalayn
источник
+1 @Jalayn для log4j упомянуть в качестве подхода, это то, что я использую, так что это укрепляет его, чтобы знать, что кто-то еще также.
therobyouknow
0

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

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

Ям Маркович
источник