Исключения «ошибки программирования» - мой подход обоснован?

9

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

Как эти два вида исключений должны рассматриваться по-разному? Считаете ли вы, что исключения ошибок должны быть четко задокументированы или достаточно для документирования соответствующих предварительных условий? И можете ли вы опустить документацию предусловия или ошибки, если это должно быть очевидно (например, ObjectDisposedExceptionпри вызове метода для удаленного объекта)

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

Ответы:

2

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

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

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

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

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

Специфичные для IDisposable, часто реализации этого интерфейса предлагают альтернативный метод, который предпочтительнее, чем явный процесс удаления. В этих случаях выделите предпочтительный метод и обратите внимание, что явное удаление не является предпочтительным.

JustinC
источник
Я понимаю необходимость документировать, какие значения разрешены, но если вы видите функцию performReadCommand(ICommand cmd, int replySizeBytes)- ожидаете ли вы, что null будет приемлемым для cmd, или отрицательное значение для replySizeBytes? Документация IMO была бы необходима только в том случае, если эти значения действительно были разрешены, и вам, вероятно, следует указать, является ли 0 допустимым для replySizeBytes.
Medo42
Они оба могут быть «действительными» в зависимости от реализации. Вы или я, и даже все здесь не ожидают, что нулевые или отрицательные значения будут соответствовать этой сигнатуре, но они могут быть. Рассмотрим среду, в которой читатель является блокирующим, постоянным процессом, и когда cmd имеет значение null, может означать, что не нужно изменять команду, используемую читателем, и продолжить следующее чтение, используя предыдущую команду. В идеале в этом случае должна быть определена и использована более подходящая сигнатура метода, которая пропускает cmd в качестве параметра, но это не так.
JustinC
2

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

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

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

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

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

Medo42
источник
1

Разумное правило:

  1. Поймайте любое исключение, которое вы можете исправить.
  2. Поймайте, прокомментируйте и отбросьте любое исключение, которое вы не можете исправить, но можете сказать что-то полезное.
  3. Не поймайте исключений, которые вы не можете обработать или диагностировать лучше, чем обработка по умолчанию.

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

Джо МакМахон
источник
0

Даже Java не «документирует» все исключения. Несмотря на то, что throwв throwsпредложении должно быть упомянуто каждое исключение n , любая строка кода может генерировать a RuntimeExceptionбез необходимости декларировать его в сигнатуре метода.

Росс Паттерсон
источник
Это противоречит популярному совету - в частности, Effective Java, 2nd ed, Item 62, «Документирует все исключения, создаваемые каждым методом». Фактически, Блох признает, что непроверенные исключения обычно относятся к ошибкам программирования, но их также следует тщательно документировать, поскольку это «лучший способ» документировать предварительные условия метода. Я не уверен, согласен ли я все же. Если я документирую исключения, я делаю их частью контракта интерфейса и, возможно, придется добавить код, чтобы они остались прежними, если моя реализация изменится.
Medo42
1
Для каждого эксперта есть контр-эксперт. Брюс Экель, автор довольно известного Thinking in Java , когда-то был в лагере с проверенными исключениями и перешел на другую сторону. Есть приятная дискуссия между Экелсом и Андерсом Хейлсбергом , создателем C #, который не проверял исключения.
Росс Паттерсон
0

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

J.Ashworth
источник
2
Считаете ли вы (например) вычисленное (или значение ошибки) NULL ошибкой программирования или ошибкой контракта ? Я иду с твоим отличием, но граница не так четкая :)
Андрей
Если значение NULL вычисляется из аргумента, который был передан (например, аргумент NULL или что-то недопустимое), это ошибка контракта. Если значение null вычислено из-за плохого кода, это ошибка программирования. Если предполагается, что значение null рассчитано, это не ошибка. Я не вижу никакой двусмысленности в этом примере.
Дж. Эшворт