Я недавно начал работать в месте с некоторыми намного более старшими разработчиками (приблизительно 50 лет). Они работали над критическими приложениями, касающимися авиации, где система не могла выйти из строя. В результате старший программист имеет тенденцию кодировать этот путь.
Он имеет тенденцию помещать логическое значение в объекты, чтобы указать, должно ли быть выброшено исключение.
пример
public class AreaCalculator
{
AreaCalculator(bool shouldThrowExceptions) { ... }
CalculateArea(int x, int y)
{
if(x < 0 || y < 0)
{
if(shouldThrowExceptions)
throwException;
else
return 0;
}
}
}
(В нашем проекте метод может потерпеть неудачу, потому что мы пытаемся использовать сетевое устройство, которое не может присутствовать в данный момент. Пример области является просто примером флага исключения)
Мне это кажется запахом кода. Написание модульных тестов становится немного сложнее, так как вам приходится каждый раз проверять наличие флага исключения. Кроме того, если что-то пойдет не так, разве вы не хотите знать сразу? Разве это не обязанность звонящего определить, как продолжить?
Его логика / рассуждение заключается в том, что наша программа должна сделать 1 вещь, показать данные пользователю. Любое другое исключение, которое не мешает нам делать это, должно игнорироваться. Я согласен, что их не следует игнорировать, но они должны всплывать и обрабатываться соответствующим человеком, и для этого не нужно разбираться с флагами.
Это хороший способ обработки исключений?
Редактировать : просто, чтобы дать больше контекста по проекту решения, я подозреваю, что это потому, что, если этот компонент выходит из строя, программа все равно может работать и выполнять свою основную задачу. Таким образом, мы бы не хотели генерировать исключение (и не обрабатывать его?), Чтобы оно закрывало программу, когда для пользователя она работает нормально.
Редактировать 2 : Чтобы дать еще больше контекста, в нашем случае вызывается метод для сброса сетевой карты. Проблема возникает, когда сетевая карта отключена и снова подключена, ей назначается другой IP-адрес, поэтому Reset выдаст исключение, потому что мы будем пытаться сбросить аппаратное обеспечение со старым IP-адресом.
Ответы:
Проблема с этим подходом состоит в том, что, хотя исключения никогда не генерируются (и, следовательно, приложение никогда не падает из-за необработанных исключений), возвращаемые результаты не обязательно являются правильными, и пользователь может никогда не знать, что существует проблема с данными (или что это за проблема и как ее исправить).
Чтобы результаты были правильными и значимыми, вызывающий метод должен проверить результат на наличие специальных чисел, т. Е. Конкретных возвращаемых значений, используемых для обозначения проблем, возникших при выполнении метода. Отрицательные (или нулевые) числа, возвращаемые для положительно определенных величин (например, площадь), являются ярким примером этого в старом коде. Если вызывающий метод не знает (или забывает!) Проверять наличие этих специальных номеров, обработка может продолжаться, даже не обнаружив ошибки. Затем данные отображаются для пользователя, показывая область 0, которая, как он знает, является неверной, но в них нет указания, что пошло не так, где и почему. Затем они задаются вопросом, являются ли какие-либо другие значения неправильными ...
Если возникнет исключение, обработка будет остановлена, ошибка (в идеале) будет зарегистрирована, и пользователь может быть уведомлен каким-либо образом. Затем пользователь может исправить все, что не так, и повторить попытку. Правильная обработка исключений (и тестирование!) Гарантирует, что критические приложения не будут аварийно завершаться или иным образом оказываться в недопустимом состоянии.
источник
Нет, я думаю, что это довольно плохая практика. Создание исключения по сравнению с возвратом значения является фундаментальным изменением в API, изменением сигнатуры метода и заставлением метода вести себя совершенно по-другому с точки зрения интерфейса.
В общем, когда мы разрабатываем классы и их API, мы должны учитывать, что
может быть несколько экземпляров класса с разными конфигурациями, плавающими в одной и той же программе в одно и то же время, и,
из-за внедрения зависимостей и любого другого числа методов программирования один клиент-потребитель может создавать объекты и передавать их другим, используя их - так часто мы имеем разделение между создателями объектов и пользователями объектов.
Теперь рассмотрим, что должен делать вызывающий метод, чтобы использовать экземпляр, который ему / ей был передан, например, для вызова метода вычисления: вызывающий должен будет проверить как нулевую область, так и исключение перехвата - ой! Вопросы тестирования касаются не только самого класса, но и обработки ошибок вызывающих ...
Мы всегда должны делать вещи максимально легкими для потребителя; эта логическая конфигурация в конструкторе, которая изменяет API метода экземпляра, является противоположностью того, чтобы потребляющий клиентский программист (возможно, вы или ваш коллега) попал в пропасть успеха.
Чтобы предложить оба API, вам гораздо лучше и более нормально либо предоставить два разных класса - один, который всегда выдает ошибку, и другой, который всегда возвращает 0 при ошибке, или предоставить два разных метода с одним классом. Таким образом, потребительский клиент может легко точно знать, как проверять и обрабатывать ошибки.
Используя два разных класса или два разных метода, вы можете гораздо проще использовать IDE для поиска пользователей методов и функций рефакторинга и т. Д., Т. К. Эти два варианта использования более не взаимосвязаны. Чтение, написание, сопровождение, обзоры и тестирование кода также проще.
С другой стороны, я лично считаю, что мы не должны принимать булевы параметры конфигурации, когда фактические вызывающие просто передают константу . Такая параметризация конфигурации объединяет два отдельных варианта использования без реальной выгоды.
Взгляните на свою базу кода и посмотрите, используется ли когда-либо переменная (или неконстантное выражение) для логического параметра конфигурации в конструкторе! Я сомневаюсь.
И дальнейшее рассмотрение состоит в том, чтобы спросить, почему вычисление области может потерпеть неудачу. Лучше всего добавить конструктор, если вычисление не может быть выполнено. Однако, если вы не знаете, можно ли выполнить вычисление до дальнейшей инициализации объекта, возможно, стоит рассмотреть возможность использования различных классов для различения этих состояний (не готовых для вычисления площади и готовой для вычисления области).
Я читал, что ваша ситуация неудачи ориентирована на удаленное взаимодействие, поэтому может не применяться; просто пища для размышлений.
Да, я согласен. Вызывающему абоненту кажется преждевременным решать, что область 0 является правильным ответом в условиях ошибки (особенно, если 0 является допустимой областью, поэтому невозможно определить разницу между ошибкой и фактическим 0, хотя это может не относиться к вашему приложению).
источник
Это интересное введение, которое создает у меня впечатление, что мотивация этого дизайна состоит в том, чтобы избегать исключения в некоторых контекстах «потому что тогда система может выйти из строя» . Но если система «может выйти из строя из-за исключения», это явный признак того, что
Так что, если программа, использующая метод,
AreaCalculator
содержит ошибки, вы, коллега, предпочитаете, чтобы программа не «рано работала при сбое», а возвращала какое-то неправильное значение (надеясь, что никто не заметит это, или надеясь, что никто не сделает с ним что-то важное). Это на самом деле маскировка ошибки , и, по моему опыту, она рано или поздно приведет к последующим ошибкам, для которых становится трудно найти основную причину.ИМХО, написание программы, которая не дает сбоя ни при каких обстоятельствах, но показывает неверные данные или результаты вычислений, обычно ничем не лучше, чем дать сбой программе. Единственный правильный подход - дать возможность вызывающему абоненту заметить ошибку, разобраться с ней, позволить ему решить, должен ли пользователь быть информирован о неправильном поведении и безопасно ли продолжать какую-либо обработку или безопаснее полностью остановить программу. Таким образом, я бы порекомендовал одно из следующего:
затрудняет упускание из виду того факта, что функция может выдать исключение. Документация и стандарты кодирования здесь ваши друзья, и регулярные обзоры кода должны поддерживать правильное использование компонентов и правильную обработку исключений.
научить команду ожидать и иметь дело с исключениями, когда они используют компоненты «черного ящика», и учитывать глобальное поведение программы.
если по каким-то причинам вы думаете, что не можете заставить вызывающий код (или разработчиков, которые его пишут) правильно использовать обработку исключений, то в качестве последнего средства вы можете разработать API с явными переменными вывода ошибок и без исключений вообще, например
так что вызывающему очень трудно игнорировать функцию, которая может потерпеть неудачу. Но это ИМХО крайне некрасиво в C #; это старый метод защитного программирования из C, в котором нет исключений, и обычно не должно быть необходимости работать в наше время.
источник
try
/catch
внутри цикла, если вам требуется обработка для продолжения в случае сбоя одного элемента.Любую функцию с n параметрами будет сложнее проверить, чем с n-1 параметрами. Распространяйте это до абсурда, и становится аргумент, что функции не должны иметь параметров вообще, потому что это облегчает их тестирование.
Хотя это хорошая идея написать код, который легко тестировать, ужасная идея поставить простоту тестирования выше написания кода, полезного для людей, которые должны его вызывать. Если в примере в вопросе есть переключатель, который определяет, будет ли выброшено исключение, возможно, что вызывающие номера, желающие такого поведения, заслуживают добавления его в функцию. Где грань между сложным и слишком сложным лежит - это суждение; Любой, кто пытается сказать вам, что есть яркая линия, которая применяется во всех ситуациях, должен с подозрением смотреть на это.
Это зависит от вашего определения неправильно. Пример в вопросе определяет неверно как «дано измерение меньше нуля и
shouldThrowExceptions
верно». Если задано измерение, если оно меньше нуля, это не так, когдаshouldThrowExceptions
ложно, потому что переключатель вызывает другое поведение. Это просто не исключительная ситуация.Настоящая проблема здесь заключается в том, что переключатель был назван плохо, потому что он не описывает, что делает функция. Если бы ему дали лучшее имя, как
treatInvalidDimensionsAsZero
, вы бы задали этот вопрос?Вызывающий действительно определяет, как продолжить. В этом случае он делает это заранее, устанавливая или сбрасывая,
shouldThrowExceptions
и функция ведет себя в соответствии со своим состоянием.Пример патологически прост, потому что он выполняет один расчет и возвращает результат. Если вы сделаете это немного более сложным, например, вычислите сумму квадратных корней списка чисел, бросание исключений может вызвать проблемы у вызывающих абонентов, которые они не могут решить. Если я передаю список
[5, 6, -1, 8, 12]
и функция выдает исключение поверх-1
, я не могу сказать, чтобы функция продолжала работать, потому что она уже прервала и выбросила сумму. Если список представляет собой огромный набор данных, создание копии без каких-либо отрицательных чисел перед вызовом функции может быть нецелесообразным, поэтому я вынужден заранее сказать, как следует обрабатывать недопустимые числа, либо в форме «просто игнорируйте их» "Переключить или, может быть, предоставить лямбда, которая вызывается для принятия этого решения.Опять же, не существует единого решения для всех. В этом примере, по-видимому, функция была написана для спецификации, в которой говорится, как обращаться с отрицательными измерениями. Последнее, что вы хотите сделать, это понизить отношение сигнал / шум в ваших журналах, заполнив их сообщениями, которые говорят: «Обычно здесь возникает исключение, но вызывающая сторона сказала, что не стоит беспокоиться».
И как один из тех гораздо более старых программистов, я бы попросил вас, пожалуйста, отойти от моего газона. ;-)
источник
our program needs to do 1 thing, show data to user. Any other exception that doesn't stop us from doing so should be ignored
мышление может привести к тому, что пользователь примет решение на основе неверных данных (потому что на самом деле программа должна сделать 1 вещь - чтобы помочь пользователю в принятии обоснованных решений) 2. Подобные случаи, какbool ExecuteJob(bool throwOnError = false)
правило, чрезвычайно подвержены ошибкам и приводят к коду, о котором трудно думать, просто читая его.Код безопасности и «нормальный» код могут привести к совершенно разным представлениям о том, как выглядит «хорошая практика». Есть много совпадений - некоторые вещи являются рискованными и их следует избегать в обоих - но все еще есть существенные различия. Если вы добавите требование, чтобы быть гарантированно отзывчивым, эти отклонения становятся весьма существенными.
Они часто относятся к вещам, которые вы ожидаете:
Для git неправильный ответ может быть очень плохим по отношению к: взятие на длинное / прерывание / зависание или даже сбой (что, по сути, не является проблемой, скажем, случайного изменения зарегистрированного кода).
Тем не менее: Для приборной панели, имеющей задержку вычисления g-силы и препятствующей выполнению расчета скорости воздушного потока, может быть неприемлемо.
Некоторые менее очевидны:
Если вы опробовали много , результаты первого порядка (например , правильные ответы) не столь большой , беспокойство , условно говоря. Вы знаете, что ваше тестирование охватит это. Однако, если было скрытое состояние или поток управления, вы не знаете, что это не будет причиной чего-то более тонкого. Это трудно исключить при тестировании.
Быть явно безопасным относительно важно. Не многие покупатели будут сомневаться в том, является ли покупаемый ими источник безопасным или нет. Если вы находитесь на рынке авиации с другой стороны ...
Как это относится к вашему примеру:
Я не знаю. Существует ряд мыслительных процессов, которые могут иметь ведущие правила, такие как «Никто не бросает в производственном коде», принятый в критически важном для безопасности коде, что было бы довольно глупо в более обычных ситуациях.
Некоторые из них касаются встраивания, некоторые - безопасность и, возможно, другие ... Некоторые из них хороши (требовались жесткие ограничения производительности и памяти), некоторые плохие (мы не обрабатываем исключения должным образом, поэтому лучше не рисковать). Большую часть времени, даже зная, почему они это сделали, на самом деле не ответит на вопрос. Например, если это связано с простотой аудита кода больше, чем на самом деле делает его лучше, это хорошая практика? Вы действительно не можете сказать. Это разные животные, и к ним нужно относиться по-разному.
Все это говорит, это выглядит немного подозрительно для меня, НО :
Решения по безопасности критически важного программного обеспечения и программного обеспечения, вероятно, не должны приниматься незнакомцами при обмене стеками разработки программного обеспечения. Вполне может быть веская причина сделать это, даже если это часть плохой системы. Не читайте слишком много во всем этом, кроме как «пища для размышлений».
источник
Иногда выбрасывать исключение - не лучший метод. Не в последнюю очередь из-за разматывания стека, но иногда из-за проблем с перехватом, особенно в языковых или интерфейсных швах.
Хороший способ справиться с этим - вернуть обогащенный тип данных. У этого типа данных достаточно состояния, чтобы описать все счастливые пути и все несчастные пути. Дело в том, что если вы взаимодействуете с этой функцией (член / глобальный / в противном случае), вы будете вынуждены обрабатывать результат.
Тем не менее, этот обогащенный тип данных не должен заставлять действовать. Представьте себе в своей области пример чего-то вроде
var area_calc = new AreaCalculator(); var volume = area_calc.CalculateArea(x, y) * z;
. Кажется полезным,volume
должен содержать площадь, умноженную на глубину - это может быть куб, цилиндр и т. Д.Но что, если сервис area_calc был недоступен? Затем
area_calc .CalculateArea(x, y)
вернул богатый тип данных, содержащий ошибку. Законно ли это умножить наz
? Это хороший вопрос. Вы можете заставить пользователей обрабатывать проверку немедленно. Это, однако, нарушает логику обработки ошибок.против
По сути, логика разбита на две строки и разделена обработкой ошибок в первом случае, тогда как во втором случае вся соответствующая логика находится в одной строке, за которой следует обработка ошибок.
Во втором случае
volume
это богатый тип данных. Это не просто число. Это увеличивает объем хранилища, иvolume
его все равно необходимо исследовать на предмет ошибки. Кроме того, онvolume
может выполнять другие вычисления, прежде чем пользователь решит обработать ошибку, что позволит ей проявиться в нескольких разных местах. Это может быть хорошо или плохо в зависимости от специфики ситуации.С другой стороны, это
volume
может быть просто простой тип данных - просто число, но что тогда происходит с ошибкой? Возможно, что значение неявно преобразуется, если оно находится в удовлетворительном состоянии. Если он находится в несчастливом состоянии, он может вернуть значение по умолчанию / ошибка (для области 0 или -1 может показаться разумным). С другой стороны, это может вызвать исключение на этой стороне границы интерфейса / языка.против
Выдавая плохое или, возможно, плохое значение, он возлагает большую ответственность на пользователя для проверки данных. Это источник ошибок, потому что для компилятора возвращаемое значение является допустимым целым числом. Если что-то не было проверено, вы обнаружите это, когда что-то пойдет не так. Второй случай сочетает в себе лучшее из обоих миров, позволяя исключениям обрабатывать несчастные пути, в то время как счастливые пути следуют за обычной обработкой. К сожалению, это заставляет пользователя обрабатывать исключения мудро, что сложно.
Просто чтобы прояснить, что Несчастный путь - это случай, неизвестный бизнес-логике (область исключения), отказ от проверки - это счастливый путь, потому что вы знаете, как обрабатывать это с помощью бизнес-правил (область правил).
Конечное решение будет таким, которое допускает все сценарии (в пределах разумного).
Что-то вроде:
источник
Я отвечу с точки зрения C ++. Я уверен, что все основные понятия могут быть перенесены в C #.
Похоже, ваш предпочтительный стиль - «всегда выбрасывать исключения»
Это может быть проблемой для кода C ++, потому что обработка исключений является тяжелой - она заставляет случай сбоя работать медленно и заставляет случай сбоя выделять память (которая иногда даже недоступна) и, как правило, делает вещи менее предсказуемыми. Тяжелый вес EH - одна из причин, по которой вы слышите, как люди говорят что-то вроде «Не используйте исключения для управления потоком».
Поэтому некоторые библиотеки (например,
<filesystem>
) используют то, что C ++ называет «двойным API», или то, что C # называетTry-Parse
шаблоном (спасибо Петру за подсказку!)Вы можете сразу увидеть проблему с «двойными API»: много дублирования кода, нет рекомендаций для пользователей относительно того, какой API «правильный» для использования, и пользователь должен сделать трудный выбор между полезными сообщениями об ошибках (
CalculateArea
) и speed (TryCalculateArea
), потому что более быстрая версия берет наше полезное"negative side lengths"
исключение и превращает его в бесполезноеfalse
- «что-то пошло не так, не спрашивайте меня, что или где». (Некоторые сдвоенные интерфейсы используют более выразительный тип ошибки, например,int errno
или Си ++std::error_code
, но это еще не говорит вам , где произошла ошибка - просто , что это действительно происходит где - то.)Если вы не можете решить, как должен вести себя ваш код, вы всегда можете довести решение до вызывающей стороны!
По сути, это то, что делает ваш коллега; за исключением того, что он выделяет «обработчик ошибок» в глобальную переменную:
Перемещение важных параметров из явных параметров функции в глобальное состояние - почти всегда плохая идея. Я это не рекомендую. (Тот факт, что это не глобальное состояние в вашем случае, а просто государство - член всего экземпляра, немного смягчает проблему, но не сильно.)
Кроме того, ваш коллега излишне ограничивает количество возможных способов обработки ошибок. Вместо того, чтобы разрешить любую лямбду обработки ошибок, он решил только два:
Это, вероятно, «кислое пятно» любой из этих возможных стратегий. Вы отняли всю гибкость у конечных пользователей, заставив их использовать один из двух ваших обратных вызовов обработки ошибок; и у вас есть все проблемы общего глобального состояния; и вы все еще платите за эту условную ветку везде.
Наконец, общее решение в C ++ (или любом языке с условной компиляцией) будет заключаться в том, чтобы заставить пользователя принимать решение для всей своей программы, глобально, во время компиляции, чтобы неиспользованный кодовый путь можно было полностью оптимизировать:
Примером того, что работает таким образом, является
assert
макрос в C и C ++, который обуславливает свое поведение в макросе препроцессораNDEBUG
.источник
std::optional
fromTryCalculateArea()
, легко объединить реализацию обеих частей двойного интерфейса в единый шаблон-функцию с флагом времени компиляции.std::expected
. Если толькоstd::optional
я неправильно пойму ваше предлагаемое решение, оно все равно будет страдать от того, что я сказал: пользователь должен сделать трудный выбор между полезными сообщениями об ошибках и скоростью, потому что более быстрая версия принимает наше полезное"negative side lengths"
исключение и превращает его в бесполезное ...false
» что-то пошло не так, не спрашивайте меня, что или где. "std::error_code *ec
проходит по всем уровням API, а затем в нижней части выполняет моральный эквивалентif (ec == nullptr) throw something; else *ec = some error code
. (Он абстрагирует фактическоеif
в нечто, называемоеErrorHandler
, но это та же самая основная идея.)Я чувствую, что следует упомянуть, откуда ваш коллега получил их образец
В настоящее время C # имеет шаблон TryGet
public bool TryThing(out result)
. Это позволяет вам получить свой результат, в то же время сообщая, является ли этот результат даже допустимым значением. (Например, всеint
значения являются допустимыми результатами дляMath.sum(int, int)
, но если значение было переполнено, этот конкретный результат мог бы быть мусором). Это относительно новая модель, хотя.Перед
out
ключевым словом вам нужно было либо сгенерировать исключение (дорого, и вызывающий должен его перехватить, либо уничтожить всю программу), создать специальную структуру (класс перед классом или дженерики были действительно полезны) для каждого результата, чтобы представить значение и возможные ошибки (отнимает много времени на создание и распространение программного обеспечения) или возвращает значение по умолчанию "error" (которое не могло быть ошибкой).Подход, используемый вашим коллегой, дает им возможность заблаговременно исключать ошибки при тестировании / отладке новых функций, одновременно обеспечивая им безопасность и производительность во время выполнения (производительность была критической проблемой все время ~ 30 лет назад), просто возвращая значение ошибки по умолчанию. Теперь это паттерн, в котором было написано программное обеспечение, и ожидаемый паттерн движется вперед, поэтому естественно продолжать делать это таким образом, хотя сейчас есть и лучшие способы. Скорее всего, этот паттерн был унаследован от возраста программного обеспечения или паттерна, в котором ваши колледжи просто не выросли (старые привычки трудно сломать).
Другие ответы уже охватывают, почему это считается плохой практикой, поэтому я просто закончу рекомендовать вам прочитать шаблон TryGet (возможно, также инкапсуляцию того, что объект должен дать своему вызывающему объекту).
источник
out
ключевым словом вы должны написатьbool
функцию, которая получает указатель на результат, то естьref
параметр. Вы могли сделать это в VB6 в 1998 году.out
Ключевое слово просто покупает вам уверенность во время компиляции, что параметр назначается, когда функция возвращается, и это все, что нужно сделать. Это является хорошим и полезным шаблон , хотя.Бывают моменты, когда вы хотите заняться его подходом, но я бы не стал считать их «нормальной» ситуацией. Ключ к определению того, в каком случае вы находитесь:
Проверьте требования. Если ваши требования фактически говорят о том, что у вас есть одна работа - показывать данные пользователю, то он прав. Однако, по моему опыту, в большинстве случаев пользователю также важно, какие данные отображаются. Они хотят правильные данные. Некоторые системы просто хотят тихо выйти из строя и позволяют пользователю понять, что что-то пошло не так, но я бы посчитал их исключением из правила.
Ключевой вопрос, который я хотел бы задать после сбоя: «Находится ли система в состоянии, когда ожидания пользователя и инварианты программного обеспечения действительны?» Если так, то непременно вернитесь и продолжайте. Практически говоря, это не то, что происходит в большинстве программ.
Что касается самого флага, флаг исключений обычно считается запахом кода, потому что пользователь должен каким-то образом знать, в каком режиме находится модуль, чтобы понять, как работает функция. Если он находится в
!shouldThrowExceptions
режиме, пользователь должен знать, что он отвечает за обнаружение ошибок и поддержание ожиданий и инвариантов при их возникновении. Они также несут ответственность прямо там и тогда, на линии, где вызывается функция. Такой флаг, как правило, сильно сбивает с толку.Однако это случается. Учтите, что многие процессоры позволяют изменять поведение с плавающей запятой внутри программы. Программа, которая хочет иметь более мягкие стандарты, может сделать это, просто изменив регистр (который фактически является флагом). Хитрость в том, что вы должны быть очень осторожны, чтобы случайно не наступить на чужие пальцы. Код часто проверяет текущий флаг, устанавливает его на желаемую настройку, выполняет операции, а затем устанавливает его обратно. Таким образом, никто не удивится переменам.
источник
Этот конкретный пример имеет интересную особенность, которая может повлиять на правила ...
То, что я вижу здесь, это проверка предварительных условий . Сбой проверки предварительных условий подразумевает ошибку выше в стеке вызовов. Таким образом, возникает вопрос: отвечает ли этот код за сообщения об ошибках, находящихся в других местах?
Некоторая напряженность здесь объясняется тем, что этот интерфейс демонстрирует примитивную одержимость -
x
иy
предположительно должен представлять реальные измерения длины. В контексте программирования, где конкретные типы доменов являются разумным выбором, мы фактически сместили бы проверку предусловий ближе к источнику данных - иными словами, взвалили бы ответственность за целостность данных на стек вызовов, где мы имеем лучший смысл для контекста.Тем не менее, я не вижу ничего принципиально неправильного в наличии двух разных стратегий управления неудачной проверкой. Я бы предпочел использовать композицию, чтобы определить, какая стратегия используется; флаг функции будет использоваться в корне композиции, а не в реализации метода библиотеки.
Национальный совет по дорожному движению и безопасности действительно хорош; Я мог бы предложить альтернативные методы реализации для серых бород, но я не склонен спорить с ними о проектировании переборок в подсистеме отчетов об ошибках.
В более широком смысле: сколько стоит бизнес? Сбой веб-сайта намного дешевле, чем жизненно важной системы.
источник
Методы либо обрабатывают исключения, либо нет, в таких языках, как C #, флаги не нужны.
Если что-то пойдет не так в ... коде, то это исключение должно быть обработано вызывающей стороной. Если никто не обрабатывает ошибку, программа будет остановлена.
В этом случае, если что-то плохое происходит в ... коде, Method1 обрабатывает проблему, и программа должна продолжаться.
Где вы обрабатываете исключения, зависит от вас. Конечно, вы можете игнорировать их, ловя и ничего не делая. Но я хотел бы убедиться, что вы игнорируете только определенные типы исключений, которые вы можете ожидать. Игнорирование (
exception ex
) опасно, потому что некоторые исключения, которые вы не хотите игнорировать, как системные исключения, связанные с нехваткой памяти и тому подобное.источник
Такой подход нарушает философию «быстро проваливайся, терпишь неудачу».
Почему вы хотите быстро потерпеть неудачу:
Недостатки не неспособность быстро и жестко:
Наконец, фактическая обработка ошибок на основе исключений имеет то преимущество, что вы можете выдавать «внешнее» сообщение об ошибке или объект, вы можете легко подключить регистрацию ошибок, предупреждений и т. Д., Не записывая ни одной дополнительной строки в код вашего домена. ,
Таким образом, существуют не только простые технические причины, но также и «системные» причины, которые делают его очень полезным для быстрого отказа.
В конце концов, в наши нынешние времена мы не будем терпеть неудачи, где обработка исключений легка и очень стабильна, а на полпути преступна. Я прекрасно понимаю, откуда приходит мысль, что хорошо подавлять исключения, но это больше не применимо.
Особенно в вашем конкретном случае, когда вы даже можете указать, следует ли создавать исключения или нет: это означает, что вызывающий должен принять решение в любом случае . Таким образом, нет никакого недостатка, чтобы вызывающая сторона перехватила исключение и обработала его соответствующим образом.
Одно замечание в комментарии:
Быстрый и жесткий сбой не означает, что все ваше приложение падает. Это означает, что в точке, где происходит ошибка, она локально дает сбой. В примере OP метод низкого уровня, вычисляющий некоторую область, не должен молча заменять ошибку неправильным значением. Это должно потерпеть неудачу ясно.
Некоторый вызывающий по цепочке, очевидно, должен поймать эту ошибку / исключение и обработать ее соответствующим образом. Если этот метод использовался в самолете, это, вероятно, должно привести к тому, что светодиод ошибки погаснет или, по крайней мере, отобразит «область вычисления ошибок» вместо неправильной области.
источник