Предупреждение! Программист C ++ приходит сюда с возможно разными идеями о том, как следует обрабатывать исключения, пытаясь ответить на вопрос, который, безусловно, касается другого языка!
Учитывая эту идею:
Например, представьте, что у нас есть ресурс fetch, который выполняет HTTP-запрос и возвращает полученные данные. И вместо ошибок, таких как ServiceTeilitaryUnavailable или RateLimitExceeded, мы просто вызовем RetryableError, предлагая потребителю просто повторить запрос и не заботиться о конкретной ошибке.
... я хотел бы предположить, что вы можете смешивать опасения, связанные с сообщением об ошибке, с курсами действий, чтобы реагировать на нее таким образом, что это может ухудшить универсальность вашего кода или потребовать много «точек перевода» для исключений. ,
Например, если я смоделирую транзакцию, связанную с загрузкой файла, она может завершиться неудачей по ряду причин. Возможно, загрузка файла включает в себя загрузку плагина, который не существует на компьютере пользователя. Возможно, файл просто поврежден, и мы столкнулись с ошибкой при его анализе.
Независимо от того, что произойдет, скажем, что действие состоит в том, чтобы сообщить о том, что случилось с пользователем, и подсказать ему, что он хочет с этим сделать («повторите попытку, загрузите другой файл, отмените»).
Метатель против Ловца
Такой порядок действий применяется независимо от того, с какой ошибкой мы столкнулись в этом случае. Он не встроен в общую идею ошибки синтаксического анализа, он не встроен в общую идею неудачной загрузки плагина. Он встроен в идею возникновения таких ошибок в точном контексте загрузки файла (сочетание загрузки файла и сбоя). Поэтому, как правило, я рассматриваю это, грубо говоря, как catcher's
ответственность за определение направления действий в ответ на выброшенное исключение (например: подсказка пользователю параметров), а не за thrower's
.
Иными словами, сайты, throw
исключения которых обычно не имеют такого рода контекстной информации, особенно если функции, которые выдают, обычно применимы. Даже в полностью выродившемся контексте, когда у них есть эта информация, вы в конечном итоге заглядываете с точки зрения поведения восстановления, встраивая ее в throw
сайт. Сайты catch
, которые обычно имеют наибольшее количество информации, доступной для определения курса действий, и дают вам одно центральное место для изменения, если этот курс действий когда-либо изменится для данной транзакции.
Когда вы начинаете пытаться генерировать исключения, которые больше не сообщают о том, что не так, а пытаются определить, что делать, это может ухудшить универсальность и гибкость вашего кода. Ошибка синтаксического анализа не всегда должна приводить к такому виду приглашения, она зависит от контекста, в котором выдается такое исключение (транзакция, в которой оно было выдано).
Метатель слепых
В целом, дизайн обработки исключений часто вращается вокруг идеи слепого метателя. Он не знает, как будет поймано исключение или где. То же самое относится и к более старым формам восстановления ошибок с использованием ручного распространения ошибок. Сайты, которые сталкиваются с ошибками, не включают в себя действия пользователя, они только встраивают минимальную информацию, чтобы сообщить, с какой ошибкой они столкнулись.
Перевернутые обязанности и обобщение ловца
Подумав об этом более тщательно, я пытался представить себе тот тип кодовой базы, где это может стать соблазном. Мое воображение (возможно, ошибочное) состоит в том, что ваша команда по-прежнему играет роль «потребителя» и реализует большую часть вызывающего кода. Возможно, у вас есть много разрозненных транзакций (много try
блоков), которые могут столкнуться с одинаковыми наборами ошибок, и все они, с точки зрения проектирования, должны привести к единому курсу действий по восстановлению.
Принимая во внимание мудрый совет из Lightness Races in Orbit's
тонкого ответа (который, я думаю, на самом деле исходит из продвинутого библиотечно-ориентированного мышления), у вас все еще может возникнуть искушение создавать исключения «что делать», только ближе к сайту восстановления транзакций.
Из этого здесь можно найти промежуточный, общий сайт обработки транзакций, который фактически централизует проблемы «что делать», но все же в контексте отлова.
Это применимо только в том случае, если вы можете разработать какую-то общую функцию, которую используют все эти внешние транзакции (например: функция, которая вводит другую функцию для вызова, или абстрактный класс базовых транзакций с переопределенным поведением, моделирующий этот сайт промежуточной транзакции, который выполняет сложный перехват). ).
Тем не менее, он может быть ответственным за централизацию действий пользователя в ответ на множество возможных ошибок, и все же в контексте ловли, а не выбрасывания. Простой пример (псевдокод Python-ish, и я совсем не опытный разработчик Python, так что может быть более идиоматический способ сделать это):
def general_catcher(task):
try:
task()
except SomeError1:
# do some uniformly-designed recovery stuff here
except SomeError2:
# do some other uniformly-designed recovery stuff here
...
[Надеюсь, с лучшим именем, чем general_catcher
]. В этом примере вы можете передать функцию, содержащую задачу, которую нужно выполнить, но при этом воспользоваться преимуществами обобщенного / унифицированного поведения перехвата для всех типов исключений, которые вас интересуют, и продолжать расширять или изменять часть «что делать». Вам нравится из этого центрального местоположения и все еще в catch
контексте, где это обычно поощряется. Лучше всего то, что мы можем помешать участкам метания относиться к себе с тем, «что делать» (сохраняя понятие «метатель слепых»).
Если вы не найдете ни одного из этих предложений здесь полезным и у вас есть сильное искушение выбросить исключения «что делать» в любом случае, в основном имейте в виду, что это, как минимум, очень антиидиоматично, а также потенциально препятствует обобщенному мышлению.