Для обработки нескольких возможных ошибок, которые не должны останавливать выполнение, у меня есть error
переменная, которую клиенты могут проверять и использовать для создания исключений. Это анти-паттерн? Есть ли лучший способ справиться с этим? Для примера этого в действии вы можете увидеть PHP mysqli API. Предположим, что проблемы видимости (средства доступа, открытая и закрытая область, переменная в классе или глобальная?) Обрабатываются правильно.
44
try
/catch
существует для. Кроме того, вы можете поместить вашtry
/catch
гораздо дальше вверх по стеку в более подходящее место для его обработки (что позволяет разделить проблемы).Ответы:
Если язык по своей природе поддерживает исключения, то предпочтительно генерировать исключения, и клиенты могут перехватить исключение, если они не хотят, чтобы оно приводило к сбою. Фактически, клиенты вашего кода ожидают исключений и будут сталкиваться со многими ошибками, потому что они не будут проверять возвращаемые значения.
Есть несколько преимуществ использования исключений, если у вас есть выбор.
Сообщения
Исключения содержат читаемые пользователем сообщения об ошибках, которые могут быть использованы разработчиками для отладки или даже отображены пользователям, если это необходимо. Если потребительский код не может обработать исключение, он всегда может записать его в журнал, чтобы разработчики могли просматривать журналы, не останавливаясь на каждой другой трассировке, чтобы выяснить, какое было возвращаемое значение, и отобразить его в таблице, чтобы выяснить, что было фактическое исключение.
С возвращаемыми значениями дополнительная информация не может быть легко предоставлена. Некоторые языки поддерживают вызовы методов для получения последнего сообщения об ошибке, поэтому эта проблема немного смягчается, но для этого требуется, чтобы вызывающий абонент делал дополнительные вызовы, а иногда требуется доступ к «специальному объекту», который несет эту информацию.
В случае сообщений об исключениях я предоставляю как можно больше контекста, например:
Сравните это с кодом возврата -85. Какой из них вы бы предпочли?
Стеки вызовов
Исключения обычно также имеют подробные стеки вызовов, которые помогают отлаживать код быстрее и быстрее, а также могут регистрироваться вызывающим кодом при желании. Это позволяет разработчикам точно определить проблему с точной линией, и, следовательно, является очень мощным. Еще раз, сравните это с файлом журнала с возвращаемыми значениями (такими как -85, 101, 0 и т. Д.), Какой из них вы бы предпочли?
Сбой быстрого предвзятого подхода
Если метод вызывается где-то, что дает сбой, он выдаст исключение. Вызывающий код должен либо явно подавить исключение, либо произойдет сбой. Я обнаружил, что это действительно удивительно, потому что во время разработки и тестирования (и даже в производстве) код быстро дает сбой, заставляя разработчиков это исправлять. В случае возвращаемых значений, если проверка на возвращаемое значение пропущена, ошибка игнорируется, и ошибка появляется где-то неожиданно, обычно с более высокой стоимостью отладки и исправления.
Обертывание и развертывание исключений
Исключения могут быть обернуты внутри других исключений, а затем развернуты при необходимости. Например, ваш код может выдать то,
ArgumentNullException
что вызывающий код может обернуть внутри,UnableToRetrievePolicyException
потому что эта операция завершилась неудачно в вызывающем коде. Хотя пользователю может отображаться сообщение, аналогичное приведенному выше примеру, некоторый диагностический код может развернуть исключение и обнаружить, чтоArgumentNullException
оно вызвало проблему, что означает ошибку кодирования в коде вашего потребителя. Это может затем запустить оповещение, чтобы разработчик мог исправить код. Такие сложные сценарии нелегко реализовать с помощью возвращаемых значений.Простота кода
Это немного сложнее объяснить, но я изучил это кодирование как с возвращаемыми значениями, так и с исключениями. Код, который был написан с использованием возвращаемых значений, обычно вызывает вызов и затем выполняет серию проверок того, что было возвращаемым значением. В некоторых случаях он будет вызывать другой метод и теперь будет иметь другую серию проверок для возвращаемых значений из этого метода. За исключением исключений, обработка исключений намного проще в большинстве, если не во всех случаях. У вас есть блоки try / catch / finally, причем среда выполнения старается изо всех сил выполнить код в блоках finally для очистки. Даже вложенные блоки try / catch / finally относительно легче отслеживать и поддерживать, чем вложенные if / else и связанные возвращаемые значения из нескольких методов.
Вывод
Если используемая вами платформа поддерживает исключения (особенно такие как Java или .NET), то вам определенно следует предположить, что нет другого способа, кроме как генерировать исключения, потому что на этих платформах есть рекомендации по созданию исключений, и ваши клиенты будут ожидать так. Если бы я использовал вашу библиотеку, я бы не стал проверять возвращаемые значения, потому что я ожидаю, что будут выданы исключения, именно таков мир на этих платформах.
Однако, если бы это был C ++, то было бы немного сложнее определить, потому что большая кодовая база уже существует с кодами возврата, а большое количество разработчиков настроено на возврат значений в отличие от исключений (например, Windows изобилует HRESULT) , Кроме того, во многих приложениях это также может быть проблемой производительности (или, по крайней мере, восприниматься как таковая).
источник
ErrorStateReturnVariable
суперкласс, и одним из его свойств являетсяInnerErrorState
(который является экземпляромErrorStateReturnVariable
), который реализует подклассы, чтобы показать цепочку ошибок ... о, подождите. : pПеременные ошибок - это реликт из языков, подобных C, где исключения не были доступны. Сегодня вам следует избегать их, за исключением случаев, когда вы пишете библиотеку, которая потенциально используется из программы на Си (или аналогичного языка без обработки исключений).
Конечно, если у вас есть тип ошибки, который лучше классифицировать как «предупреждение» (= ваша библиотека может выдать действительный результат, а вызывающий может проигнорировать предупреждение, если он считает, что это не важно), тогда индикатор состояния в форме переменной может иметь смысл даже в языках с исключениями. Но будьте осторожны. Призыватели библиотеки склонны игнорировать такие предупреждения, даже если они не должны. Так что подумайте дважды, прежде чем вводить такую конструкцию в свою библиотеку.
источник
Есть несколько способов сообщить об ошибке:
Проблема переменной error в том, что ее легко забыть проверить.
Проблема исключений заключается в том, что они создают скрытые пути выполнения, и хотя try / catch легко написать, обеспечить правильное восстановление в предложении catch действительно сложно (без поддержки систем типов / компиляторов).
Проблема обработчиков условий в том, что они плохо сочетаются: если у вас динамическое выполнение кода (виртуальные функции), то невозможно предсказать, какие условия следует обрабатывать. Кроме того, если одно и то же условие может быть выдвинуто в нескольких точках, нельзя сказать, что единообразное решение может применяться каждый раз, и оно быстро становится грязным.
Полиморфные возвраты (
Either a b
в Хаскеле) - мое любимое решение:Единственная проблема заключается в том, что они могут привести к чрезмерной проверке; языки, которые их используют, имеют идиомы, чтобы связывать вызовы функций, которые их используют, но это все же может потребовать немного большего набора / беспорядка. В Хаскеле это были бы монады ; однако, это гораздо страшнее, чем кажется, см. Железнодорожно-ориентированное программирование .
источник
Я думаю, что это ужасно. В настоящее время я занимаюсь рефакторингом приложения Java, которое использует возвращаемые значения вместо исключений. Хотя вы, возможно, вообще не работаете с Java, я думаю, что это, тем не менее, применимо.
Вы получите код, подобный следующему:
Или это:
Я бы предпочел, чтобы действия сами создавали исключения, так что вы получите что-то вроде:
Вы можете обернуть это в try-catch и получить сообщение из исключения, или вы можете игнорировать исключение, например, когда вы удаляете что-то, что уже может быть пропущено. Он также сохраняет вашу трассировку стека, если она у вас есть. Сами методы тоже становятся проще. Вместо того, чтобы обрабатывать сами исключения, они просто бросают то, что пошло не так.
Текущий (ужасный) код:
Новый и улучшенный:
Трассировка треков сохраняется и сообщение доступно в исключении, а не в бесполезном «Что-то пошло не так!».
Вы, конечно, можете предоставить лучшие сообщения об ошибках, и вы должны. Но этот пост здесь, потому что текущий код, над которым я работаю, - боль, и вы не должны делать то же самое.
источник
throw new Exception("Something went wrong with " + instanceVar, ex);
«Чтобы справиться с несколькими возможными ошибками, это не должно останавливать выполнение»
Если вы имеете в виду, что ошибки не должны останавливать выполнение текущей функции, а должны каким-то образом сообщаться вызывающей стороне - тогда у вас есть несколько вариантов, которые на самом деле не были упомянуты. Этот случай действительно больше предупреждение, чем ошибка. Бросать / возвращать не вариант, так как это завершает текущую функцию. Один параметр сообщения об ошибке или возврат только позволяет не более одной из этих ошибок.
Два шаблона, которые я использовал:
Коллекция ошибок / предупреждений, переданная или сохраненная как переменная-член. К которому вы добавляете вещи и просто продолжаете обрабатывать. Мне лично не очень нравится такой подход, так как я чувствую, что он лишает звонящего абонента.
Передайте объект обработчика ошибок / предупреждений (или установите его как переменную-член). И каждая ошибка вызывает функцию-член обработчика. Таким образом, вызывающая сторона может решить, что делать с такими не прекращающимися ошибками.
То, что вы передаете этим коллекциям / обработчикам, должно содержать достаточно контекста для того, чтобы ошибка была обработана «правильно» - строка обычно слишком мала, передавая какой-то экземпляр Exception, часто разумно - но иногда осуждаемый (как злоупотребление исключениями) ,
Типичный код с использованием обработчика ошибок может выглядеть следующим образом
источник
warnings
пакет Python , который дает другой шаблон для этой проблемы.Часто нет ничего плохого в том, чтобы использовать этот шаблон или этот шаблон, если вы используете шаблон, который используют все остальные. В разработке Objective-C наиболее предпочтительным шаблоном является передача указателя, в котором вызываемый метод может поместить объект NSError. Исключения зарезервированы для ошибок программирования и приводят к сбою (если только у вас нет программистов на Java или .NET, пишущих свое первое приложение для iPhone). И это работает довольно хорошо.
источник
На вопрос уже дан ответ, но я не могу с собой поделать.
Вы не можете ожидать, что Exception предоставит решение для всех вариантов использования. Молот кто-нибудь?
Существуют случаи, когда исключения не являются концом всего и являются всем, например, если метод получает запрос и отвечает за проверку всех переданных полей, а не только первого, тогда вы должны думать, что это должно быть возможно указать причину ошибки для более чем одного поля. Должна также быть возможность указать, мешает ли характер проверки пользователю идти дальше или нет. Примером этого может быть ненадежный пароль. Вы можете показать пользователю сообщение о том, что введенный пароль не очень надежный, но достаточно сильный.
Можно утверждать, что все эти проверки могут быть выброшены как исключение в конце модуля проверки, но они будут кодами ошибок во всем, кроме имени.
Итак, урок здесь: исключения имеют свои места, как и коды ошибок. Выбрал с умом.
источник
Validator
(интерфейс), внедренный в рассматриваемый метод (или объект позади него). В зависимости от введенногоValidator
метода метод будет продолжаться с неверными паролями - или нет. Окружающий код может затем попытаться выполнить,WeakValidator
если пользователь запросил его, например, после того, какWeakPasswordException
был сгенерирован первоначально попытаннымStrongValidator
.MiddlyStrongValidator
или что-то. И если это на самом деле не прерывало ваш поток, онValidator
должен быть вызван заранее, то есть до того, как продолжить поток, пока пользователь все еще вводит свой пароль (или аналогичный). Но тогда проверка не была частью рассматриваемого метода в первую очередь. :) Вероятно, дело вкуса в конце концов ...AggregateException
(или подобноеValidationException
) и помещаю конкретные исключения для каждой проблемы валидации в InnerExceptions. Например, это может бытьBadPasswordException
: «Пароль пользователя меньше минимальной длины 6» илиMandatoryFieldMissingException
: «Имя пользователя должно быть предоставлено» и т. Д. Это не эквивалентно кодам ошибок. Все эти сообщения могут отображаться пользователю таким образом, чтобы они его понимали, и еслиNullReferenceException
вместо этого было выброшено a , мы получили ошибку.Есть варианты использования, где коды ошибок предпочтительнее исключений.
Если ваш код может продолжаться, несмотря на ошибку, но требует создания отчетов, то исключение - плохой выбор, потому что исключения прекращают поток. Например, если вы читаете файл данных и обнаруживаете, что он содержит неконтактную часть неверных данных, может быть лучше прочитать остальную часть файла и сообщить об ошибке, а не сразу потерпеть неудачу.
Другие ответы объясняли, почему исключения должны быть предпочтительнее кодов ошибок в целом.
источник
AcknowledgePossibleCorruption
метода. .Нет ничего плохого в том, чтобы не использовать исключения, когда исключения не подходят.
Когда выполнение кода не должно прерываться (например, воздействие на пользовательский ввод, который может содержать несколько ошибок, например, программу для компиляции или форму для обработки), я обнаруживаю, что сбор ошибок в переменных ошибок, как
has_errors
иerror_messages
действительно, гораздо более элегантный дизайн, чем бросание исключение при первой ошибке. Это позволяет находить все ошибки при вводе пользователем, не заставляя пользователя повторно отправлять данные без необходимости.источник
В некоторых динамических языках программирования вы можете использовать как значения ошибок, так и обработку исключений . Это делается путем возврата неискаженного объекта исключения вместо обычного возвращаемого значения, которое можно проверить как значение ошибки, но оно выдает исключение, если оно не проверено.
В Perl 6 это делается через
fail
, который, если вno fatal;
области видимости, возвращает специальный неисключенныйFailure
объект исключения .В Perl 5 вы можете использовать Contextual :: Return, с которым вы можете сделать это
return FAIL
.источник
Если нет чего-то особенного, я думаю, что наличие переменной ошибки для проверки - плохая идея. Похоже, цель состоит в том, чтобы сэкономить время, потраченное на проверку (вы можете просто вернуть значение переменной)
Но затем, если вы что-то измените, вам все равно придется пересчитать это значение. Я не могу сказать больше о остановке и броске исключений.
РЕДАКТИРОВАТЬ: я не понимал, что это вопрос программной парадигмы, а не конкретного случая.
Позвольте мне дополнительно уточнить мои пункты в моем конкретном случае, в котором мой ответ будет иметь смысл
Есть два вида ошибок:
На уровне сервиса нет другого выбора, кроме как использовать объект Result в качестве оболочки, которая является эквивалентом переменной ошибки. Имитация исключения через сервисный вызов по протоколу, подобному http, возможна, но это определенно не очень хорошая вещь. Я не говорю об ошибке такого рода и не думаю, что это та ошибка, которую задают в этом вопросе.
Я думал о втором типе ошибки. И мой ответ об этом втором виде ошибок. В объектах сущностей есть выбор для нас, некоторые из них
Использование переменной проверки аналогично наличию единого метода проверки для каждого объекта сущности. В частности, пользователь может установить значение таким образом, чтобы установщик оставался чисто установочным, без побочных эффектов (это часто является хорошей практикой) или кто-то может включить проверку в каждый установщик, а затем сохранить результат в переменной проверки. Преимущество этого состоит в экономии времени, результат проверки кэшируется в переменную проверки, поэтому, когда пользователь вызывает validation () несколько раз, нет необходимости выполнять множественную проверку.
Лучшее, что можно сделать в этом случае, - это использовать один метод проверки, даже не используя никакой проверки для кэширования ошибки проверки. Это помогает сохранить сеттер просто сеттером.
источник