У меня есть исключения, созданные для каждого условия, которое мое приложение не ожидает. UserNameNotValidException
и PasswordNotCorrectException
т. д.
Однако мне сказали, что я не должен создавать исключения для этих условий. В моем UML эти ARE являются исключениями для основного потока, так почему же это не должно быть исключением?
Любое руководство или лучшие практики для создания исключений?
exception
language-agnostic
Kwan Cheng
источник
источник
IsCredentialsValid(username,password)
не должен выдавать исключение, если имя пользователя или пароль недействительны, но должен возвращать false. Но, скажем, метод, который считывает данные из базы данных, может на законных основаниях выдать такое исключение, если аутентификация не удалась. Вкратце: вы должны вызвать исключение, если метод не может выполнить задачу, которую он должен делать.Ответы:
Моя личная рекомендация: исключение выдается, когда фундаментальное предположение о текущем блоке кода оказывается ложным.
Пример 1: скажем, у меня есть функция, которая должна проверять произвольный класс и возвращать true, если этот класс наследует от List <>. Эта функция задает вопрос: «Является ли этот объект потомком List?» Эта функция никогда не должна генерировать исключение, потому что в ее работе нет серых областей - каждый отдельный класс наследует или не наследует от List <>, поэтому ответ всегда «да» или «нет».
Пример 2: скажем, у меня есть другая функция, которая проверяет List <> и возвращает true, если его длина больше 50, и false, если длина меньше. Эта функция задает вопрос: «Есть ли в этом списке более 50 элементов?» Но этот вопрос делает предположение - он предполагает, что объект, который ему дан, является списком. Если я передаю ему значение NULL, то это предположение неверно. В том случае, если функция возвращает либо верно или неверно, то это нарушение свои собственные правила. Функция не может возвращать ничего и утверждает , что он ответил на вопрос правильно. Так что он не возвращается - он вызывает исключение.
Это сравнимо с логической ошибкой «нагруженного вопроса» . Каждая функция задает вопрос. Если вводимые данные делают этот вопрос ошибочным, выведите исключение. Эту линию труднее нарисовать с помощью функций, которые возвращают void, но суть в следующем: если предположения функции относительно ее входных данных нарушаются, она должна генерировать исключение вместо нормального возврата.
Другая сторона этого уравнения: если вы обнаружите, что ваши функции часто генерируют исключения, вам, вероятно, нужно уточнить их допущения.
источник
Потому что это вещи, которые будут происходить нормально. Исключения не являются механизмами управления потоком. Пользователи часто неправильно вводят пароли, это не исключительный случай. Исключения должны быть действительно редкой вещью,
UserHasDiedAtKeyboard
типа ситуаций.источник
Мои маленькие рекомендации находятся под сильным влиянием великой книги "Код завершен":
источник
Это НЕ исключение, если имя пользователя неверно или пароль неверен. Это то, что вы должны ожидать в нормальном режиме работы. Исключения составляют вещи, которые не являются частью нормальной работы программы и встречаются довольно редко.
РЕДАКТИРОВАТЬ: я не люблю использовать исключения, потому что вы не можете сказать, если метод выдает исключение, просто посмотрев на вызов. Вот почему исключения следует использовать только в том случае, если вы не можете справиться с ситуацией приличным образом (подумайте «недостаточно памяти» или «компьютер горит»).
источник
Одно из практических правил - использовать исключения в случае чего-то, что вы обычно не можете предсказать. Примерами являются подключение к базе данных, отсутствие файла на диске и т. Д. Для сценариев, которые вы можете предсказать, например, пользователи, пытающиеся войти в систему с неверным паролем, должны использовать функции, которые возвращают логические значения и знают, как корректно справиться с ситуацией. Вы не хотите внезапно завершать выполнение, вызывая исключение только потому, что кто-то набрал неверный пароль.
источник
Другие предлагают, чтобы исключения не использовались, потому что неправильный вход в систему следует ожидать в нормальном потоке, если пользователь ошибается. Я не согласен, и я не понимаю причины. Сравните это с открытием файла ... если файл не существует или недоступен по какой-либо причине, то исключительная ситуация будет выдана платформой. Использование вышеупомянутой логики было ошибкой Microsoft. Они должны были вернуть код ошибки. То же самое для разбора, веб-запросов и т. Д. И т. Д.
Я не считаю плохой логин частью нормального потока, это исключение. Обычно пользователь вводит правильный пароль, и файл существует. Исключительные случаи являются исключительными, и вполне допустимо использовать исключения для них. Усложнение вашего кода путем распространения возвращаемых значений через n уровней вверх по стеку является пустой тратой энергии и приведет к грязному коду. Сделайте самое простое, что может сработать. Не преждевременно оптимизируйте, используя коды ошибок, исключительные вещи по определению случаются редко, а исключения ничего не стоят, если вы их не выбросите.
источник
login
метода -типа будет то, что пароль может быть неправильным, он может фактически использоваться для определения этого и не хотел бы иметь исключение в этом случае; тогда как вfile open
сценарии типа, который имеет определенный желаемый результат - если система не может доставить результат из-за неправильных входных параметров или какого-либо внешнего фактора, это вполне логичное использование исключения.Исключения являются довольно дорогостоящим эффектом, если, например, у вас есть пользователь, предоставивший неверный пароль, обычно лучше передать обратно флаг сбоя или какой-либо другой индикатор того, что он недействителен.
Это связано с тем, что исключения обрабатываются, действительно неверный ввод и уникальные критические элементы останова должны быть исключениями, а не ошибочной информацией входа в систему.
источник
Я думаю, что вы должны генерировать исключение только тогда, когда вы ничего не можете сделать, чтобы выйти из своего текущего состояния. Например, если вы выделяете память, а выделять ее нет. В тех случаях, когда вы упоминаете, вы можете четко восстановиться после этих состояний и соответственно вернуть код ошибки своему абоненту.
Вы увидите множество советов, в том числе в ответах на этот вопрос, что вы должны бросать исключения только в «исключительных» обстоятельствах. Это кажется поверхностно разумным, но это ошибочный совет, потому что он заменяет один вопрос («когда я должен выбросить исключение») другим субъективным вопросом («что является исключительным»). Вместо этого следуйте совету Херба Саттера (для C ++, доступного в статье доктора Доббса « Когда и как использовать исключения» , а также в его книге с Андреем Александреску, « Стандарты кодирования C ++» ): выведите исключение тогда и только тогда, когда
Почему это лучше? Разве это не заменяет вопрос несколькими вопросами о предварительных условиях, постусловиях и инвариантах? Это лучше по нескольким связанным причинам.
throw
- деталью реализации. Это заставляет нас иметь в виду, что мы должны рассматривать проект и его реализацию отдельно, и наша задача при реализации метода заключается в создании чего-то, что удовлетворяет проектным ограничениям.catch
пункты.источник
Я бы сказал, что нет жестких и быстрых правил, когда следует использовать исключения. Однако есть веские причины использовать или не использовать их:
Причины использования исключений:
Причины не использовать исключения:
В целом, я был бы более склонен использовать исключения в Java, чем в C ++ или C #, потому что я придерживаюсь мнения, что исключение, объявленное или нет, является фундаментальной частью формального интерфейса функции, поскольку изменение гарантии исключения может сломать вызывающий код. Самым большим преимуществом их использования в Java IMO является то, что вы знаете, что вызывающая сторона ДОЛЖНА обработать исключение, и это повышает вероятность правильного поведения.
Из-за этого на любом языке я всегда получал бы все исключения в слое кода или API из общего класса, так что вызывающий код всегда может гарантировать перехват всех исключений. Также я бы посчитал плохим выбрасывать классы исключений, специфичные для реализации, при написании API или библиотеки (т. Е. Переносить исключения из нижних уровней, чтобы исключение, которое получает ваш вызывающий объект, было понятно в контексте вашего интерфейса).
Обратите внимание, что Java делает различие между общими и исключениями времени выполнения в том смысле, что последние не нужно объявлять. Я бы использовал классы исключений времени выполнения только тогда, когда вы знаете, что ошибка является результатом ошибки в программе.
источник
Классы исключений похожи на «нормальные» классы. Вы создаете новый класс, когда он «является» объектом другого типа, с разными полями и разными операциями.
Как правило, вы должны попытаться найти баланс между количеством исключений и гранулярностью исключений. Если ваш метод генерирует более 4-5 различных исключений, вы, вероятно, можете объединить некоторые из них в более «общие» исключения (например, в вашем случае «AuthenticationFailedException») и использовать сообщение об исключении, чтобы подробно описать, что пошло не так. Если ваш код не обрабатывает каждый из них по-разному, вам не нужно создавать много классов исключений. И если это произойдет, пусть вы просто вернете перечисление с ошибкой, которая произошла. Так немного чище.
источник
Если это код, выполняющийся внутри цикла, который, вероятно, будет вызывать исключение снова и снова, то создание исключений не очень хорошая вещь, потому что они довольно медленные для больших N. Но нет ничего плохого в создании пользовательских исключений, если производительность не проблема. Просто убедитесь, что у вас есть базовое исключение, которое они все наследуют, называемое BaseException или что-то подобное. BaseException наследует System.Exception, но все ваши исключения наследуют BaseException. Вы даже можете иметь дерево типов исключений для группировки похожих типов, но это может быть или не быть чрезмерным.
Итак, краткий ответ таков: если это не приводит к значительному снижению производительности (чего не должно быть, если вы не выбросите много исключений), тогда продолжайте.
источник
int.MaxValue
и генерирует в нем исключение «деление на ноль». Версия IF / ELSE, в которой я проверял, является ли дивиденд не нулевым, перед операцией деления , завершенной за 6082 мс и 15407722 тиков, тогда как версия TRY / CATCH, в которой я генерировал исключение и перехватывал исключение , завершилась в 28174385 мс и 71371326155 галочки: колоссальные в 4632 раза больше, чем версия if / else.Я согласен с Джаплоком, если вы не уверены в исходе операции. Вызовы API, доступ к файловым системам, вызовы базы данных и т. Д. Каждый раз, когда вы пересекаете «границы» ваших языков программирования.
Я хотел бы добавить, не стесняйтесь бросить стандартное исключение. Если вы не собираетесь делать что-то «другое» (игнорировать, отправлять по электронной почте, регистрировать, показывать, что твиттер изображает кита и т. Д.), То не беспокойтесь о пользовательских исключениях.
источник
Практическое правило создания исключений довольно простое. Вы делаете это, когда ваш код перешел в НЕИЗВЕСТНОЕ НЕДЕЙСТВИТЕЛЬНОЕ состояние. если данные скомпрометированы или вы не можете свернуть обработку, которая произошла до этой точки, вы должны прекратить ее. действительно, что еще вы можете сделать? ваша логика обработки в конечном итоге потерпит неудачу в другом месте. если вы можете как-то восстановиться, делайте это и не бросайте исключения.
в вашем конкретном случае, если вы были вынуждены сделать что-то глупое, например, принять снятие денег и только после этого проверить пользователя / пароль, вы должны прекратить процесс, выдав исключение, чтобы сообщить, что произошло что-то плохое, и предотвратить дальнейший ущерб.
источник
Как правило, вы хотите создать исключение для всего, что может произойти в вашем приложении, которое является «Исключительным».
В вашем примере оба этих исключения выглядят так, будто вы вызываете их с помощью проверки пароля / имени пользователя. В этом случае можно утверждать, что на самом деле не исключено, что кто-то введет неверное имя пользователя / пароль.
Они являются «исключениями» для основного потока вашего UML, но являются более «ветвями» в обработке.
Если вы попытались получить доступ к файлу или базе данных passwd и не смогли, это было бы исключительным случаем и потребовало бы исключения.
источник
Во-первых, если пользователи вашего API не заинтересованы в конкретных мелкозернистых ошибках, то наличие особых исключений для них не имеет никакого значения.
Поскольку зачастую невозможно узнать, что может быть полезно для ваших пользователей, лучше использовать конкретные исключения, но обеспечить их наследование от общего класса (например, std :: exception или его производных в C ++). Это позволяет вашему клиенту ловить определенные исключения, если они захотят, или более общее исключение, если им все равно.
источник
Исключения предназначены для событий, которые представляют собой ненормальное поведение, ошибки, сбои и тому подобное. Функциональное поведение, ошибка пользователя и т. Д. Должны обрабатываться логикой программы. Так как неверная учетная запись или пароль являются ожидаемой частью логического потока в процедуре входа в систему, она должна иметь возможность обрабатывать эти ситуации без исключений.
источник
У меня философские проблемы с использованием исключений. По сути, вы ожидаете, что произойдет конкретный сценарий, но вместо того, чтобы обращаться с ним явно, вы отталкиваете проблему от решения «в другом месте». И где это «где-то еще», можно догадаться.
источник
Я бы сказал, что, как правило, каждый фундаментализм ведет в ад.
Вы, конечно, не захотите закончить потоком, управляемым исключениями, но вообще избегать исключений - тоже плохая идея. Вы должны найти баланс между обоими подходами. Что бы я не делал, это создавал тип исключения для каждой исключительной ситуации. Это не продуктивно.
Обычно я предпочитаю создавать два основных типа исключений, которые используются во всей системе: LogicalException и TechnicalException . Их можно дополнительно различать по подтипам, если это необходимо, но обычно это не является необходимым.
Техническое исключение означает действительно неожиданное исключение, такое как отключение сервера базы данных, подключение к веб-службе, возникновение исключения IOException и т. Д.
С другой стороны, логические исключения используются для распространения менее серьезной ошибочной ситуации на верхние уровни (как правило, некоторый результат проверки).
Обратите внимание, что даже логическое исключение не предназначено для регулярного использования для управления потоком программы, а скорее для того, чтобы выделить ситуацию, когда поток действительно должен закончиться. При использовании в Java оба типа исключений являются RuntimeException подклассами а обработка ошибок в значительной степени ориентирована на аспекты.
Поэтому в примере входа в систему может быть целесообразно создать что-то вроде AuthenticationException и различать конкретные ситуации по значениям перечисления, таким как UsernameNotExisting , PasswordMismatch и т. Д. Тогда у вас не будет огромной иерархии исключений, и вы сможете поддерживать блоки catch на поддерживаемом уровне. , Вы также можете легко использовать некоторый общий механизм обработки исключений, так как вы классифицировали исключения и хорошо знаете, что распространять до пользователя и как.
Мы обычно используем исключение LogicalException во время вызова веб-службы, когда ввод пользователя был неверным. Исключение передается на детали SOAPFault, а затем снова отправляется на исключение на клиенте, что приводит к отображению ошибки проверки в одном поле ввода определенной веб-страницы, поскольку исключение имеет правильное сопоставление с этим полем.
Это, конечно, не единственная ситуация: вам не нужно нажимать веб-сервис, чтобы вызвать исключение. Вы можете сделать это в любой исключительной ситуации (например, в случае необходимости быстрого отказа) - все это на ваше усмотрение.
источник
для меня должно быть выброшено исключение в случае сбоя требуемого технического или бизнес-правила. например, если сущность автомобиля связана с массивом из 4 шин ... если одна или несколько шин имеют нулевое значение ... должно быть сгенерировано исключение "NotEnoughTiresException", поскольку оно может быть перехвачено на другом уровне системы и иметь значительный смысл через логирование. кроме того, если мы просто попытаемся контролировать поток на ноль и предотвратить возникновение машины. мы никогда не сможем найти источник проблемы, потому что шина изначально не должна быть нулевой.
источник
Основная причина, по которой следует избегать выбрасывания исключения, состоит в том, что при создании исключения возникает много накладных расходов.
В статье ниже утверждается, что исключение - исключительные условия и ошибки.
Неправильное имя пользователя - это не обязательно ошибка программы, а ошибка пользователя ...
Вот хорошая отправная точка для исключений в .NET: http://msdn.microsoft.com/en-us/library/ms229030(VS.80).aspx
источник
Создание исключений приводит к разматыванию стека, что оказывает некоторое влияние на производительность (допустим, современные управляемые среды улучшили это). Все еще неоднократно бросать и ловить исключения во вложенной ситуации было бы плохой идеей.
Вероятно, более важно то, что исключения предназначены для исключительных условий. Они не должны использоваться для обычного потока управления, потому что это ухудшит читабельность вашего кода.
источник
У меня есть три типа условий, которые я ловлю.
Плохой или отсутствующий ввод не должен быть исключением. Используйте js на стороне клиента и регулярное выражение на стороне сервера для обнаружения, установки атрибутов и пересылки обратно на ту же страницу с сообщениями.
AppException. Обычно это исключение, которое вы обнаруживаете и добавляете в свой код. Другими словами, это те, которые вы ожидаете (файл не существует). Зарегистрируйте его, установите сообщение и вернитесь на страницу с общей ошибкой. На этой странице обычно есть немного информации о том, что произошло.
Неожиданное исключение. Это те, о которых вы не знаете. Зарегистрируйте это с деталями и отправьте их на страницу с общей ошибкой.
Надеюсь это поможет
источник
Безопасность связана с вашим примером: вы не должны сообщать злоумышленнику, что имя пользователя существует, но пароль неправильный. Это дополнительная информация, которой вам не нужно делиться. Просто скажите «имя пользователя или пароль неверны».
источник
Ответ прост: всякий раз, когда операция невозможна (либо из-за приложения, либо из-за нарушения бизнес-логики). Если метод вызывается и невозможно сделать то, для чего был написан метод, выдается исключение. Хорошим примером является то, что конструкторы всегда выдают ArgumentExceptions, если экземпляр не может быть создан с использованием предоставленных параметров. Другой пример - InvalidOperationException, который генерируется, когда операция не может быть выполнена из-за состояния другого члена или членов класса.
В вашем случае, если вызывается такой метод, как Login (имя пользователя, пароль), если имя пользователя недопустимо, действительно корректно выбрасывать исключение UserNameNotValidException или PasswordNotCorrectException, если пароль неверен. Пользователь не может войти в систему с помощью предоставленных параметров (т.е. это невозможно, потому что это нарушит аутентификацию), поэтому выведите исключение. Хотя, возможно, ваши два исключения унаследованы от ArgumentException.
Сказав это, если вы не хотите генерировать исключение, потому что сбой входа в систему может быть очень распространенным, одна стратегия вместо этого создать метод, который возвращает типы, которые представляют различные сбои. Вот пример:
Большинство разработчиков учат избегать исключений из-за накладных расходов, вызванных их выбросом. Здорово быть внимательным к ресурсам, но обычно не в ущерб дизайну вашего приложения. Это, вероятно, причина, по которой вам сказали не бросать ваши два исключения. Использовать ли исключения или нет обычно сводится к тому, как часто будет происходить исключение. Если это довольно распространенный или вполне ожидаемый результат, то это когда большинство разработчиков избегают исключений и вместо этого создают другой метод для указания сбоя из-за предполагаемого потребления ресурсов.
Вот пример того, как избежать использования Исключений в сценарии, как только что описано, с помощью шаблона Try ():
источник
На мой взгляд, фундаментальный вопрос должен заключаться в том, следует ли ожидать, что вызывающая сторона захочет продолжить нормальный поток программ, если возникает условие. Если вы не знаете, либо у вас есть отдельные методы doSomething и trySomething, где первый возвращает ошибку, а второй нет, либо есть подпрограмма, которая принимает параметр, указывающий, следует ли генерировать исключение в случае сбоя). Рассмотрим класс для отправки команд в удаленную систему и отчета об ответах. Определенные команды (например, перезапуск) заставят удаленную систему отправить ответ, но затем не будут отвечать на запросы в течение определенного периода времени. Таким образом, полезно иметь возможность отправлять команду «ping» и выяснять, отвечает ли удаленная система в течение разумного промежутка времени без необходимости генерировать исключение, если это не так. t (вызывающий может ожидать, что первые несколько попыток "ping" потерпят неудачу, но одна из них в конечном итоге сработает). С другой стороны, если есть последовательность команд, например:
Хотелось бы, чтобы сбой любой операции прерывал всю последовательность. Хотя можно проверить каждую операцию, чтобы убедиться в ее успешности, более полезно, чтобы процедура exchange_command () выдавала исключение в случае сбоя команды.
На самом деле, в приведенном выше сценарии может быть полезно иметь параметр для выбора нескольких режимов обработки ошибок: никогда не генерировать исключения, генерировать исключения только для ошибок связи или генерировать исключения в любых случаях, когда команда не возвращает «успех» Индикация
источник
«PasswordNotCorrectException» не является хорошим примером для использования исключений. Ожидается, что пользователи неправильно вводят свои пароли, поэтому вряд ли это исключение. Вы, вероятно, даже восстанавливаетесь после этого, показывая хорошее сообщение об ошибке, так что это просто проверка правильности.
Необработанные исключения в конечном итоге остановят выполнение - и это хорошо. Если вы возвращаете ложные, нулевые коды или коды ошибок, вам придется самостоятельно решать состояние программы. Если вы забудете где-нибудь проверить условия, ваша программа может продолжать работать с неверными данными, и вам может быть трудно выяснить, что произошло и где .
Конечно, вы можете вызвать ту же проблему с пустыми операторами catch, но, по крайней мере, определить их проще и не требует от вас понимания логики.
Итак, как правило:
Используйте их там, где вы не хотите или просто не можете восстановиться после ошибки.
источник
Вы можете использовать несколько общих исключений для этих условий. Например, ArgumentException предназначен для использования, когда что-то не так с параметрами в методе (за исключением ArgumentNullException). Обычно вам не нужны исключения, такие как LessThanZeroException, NotPrimeNumberException и т. Д. Подумайте о пользователе вашего метода. Число условий, которые она хочет обработать специально, равно количеству типов исключений, которые должен выдавать ваш метод. Таким образом, вы можете определить, насколько подробными будут ваши исключения.
Кстати, всегда старайтесь предоставить пользователям ваших библиотек несколько способов избежать исключений. TryParse - хороший пример, он существует для того, чтобы вам не приходилось использовать int.Parse и перехватывать исключение. В вашем случае вы можете захотеть предоставить некоторые методы для проверки правильности имени пользователя или пароля, чтобы вашим пользователям (или вам) не приходилось много обрабатывать исключения. Надеемся, что это приведет к более читаемому коду и лучшей производительности.
источник
В конечном счете, решение сводится к тому, будет ли более полезным иметь дело с ошибками на уровне приложений, такими как эта, с помощью обработки исключений или с помощью собственного собственного механизма, такого как возврат кодов состояния. Я не думаю, что есть жесткое правило о том, что лучше, но я бы подумал:
источник
Существует два основных класса исключений:
1) Системное исключение (например, потеря соединения с базой данных) или 2) Пользовательское исключение. (например, подтверждение ввода пользователя, «пароль неверный»)
Я обнаружил, что полезно создать свой собственный класс исключений пользователя, и когда я хочу выдать ошибку пользователя, я хочу, чтобы ее обрабатывали по-другому (т.е. ошибка ресурса отображалась для пользователя), тогда все, что мне нужно сделать в моем основном обработчике ошибок, это проверить тип объекта. :
источник
Некоторые полезные вещи, которые следует учитывать при принятии решения о целесообразности исключения:
какой уровень кода вы хотите запустить после появления кандидата на исключение, то есть сколько слоев стека вызовов должно быть разобрано. Как правило, вы хотите обработать исключение как можно ближе к месту его возникновения. Для проверки имени пользователя и пароля вы обычно обрабатываете сбои в одном и том же блоке кода, а не позволяете появиться исключению. Так что исключение, вероятно, не подходит. (OTOH, после трех неудачных попыток входа в систему поток управления может сместиться в другое место, и здесь может быть уместно исключение.)
Это событие вы хотели бы видеть в журнале ошибок? Не каждое исключение записывается в журнал ошибок, но полезно спросить, будет ли полезна эта запись в журнале ошибок - то есть вы попытаетесь что-то с этим сделать или будете мусором, который вы игнорируете.
источник