О перехвате исключений
Перехватывайте исключения только если у вас есть “план Б”. Другими словами, есть альтернативный способ решить задачу. Если его нет, то ловить исключение не нужно.
Практика показывает, что “плана Б” почти никогда не бывает. Сценарий всегда один: взяли то, получили другое, прибавили третье — вот и результат. Отсюда следствие: поймав исключение, вы ничего толком не сделаете.
Я уже приводил примеры. Нет файла на диске — с этим ничего поделать нельзя. Нет файла в S3 — тоже. Не задана переменная среды (пароль или токен) — без нее никуда не пойдешь.
Почти каждая ошибка означает, что задача в целом провалена, и пытаться дальше бесполезно.
По этой причине, кстати, в Джаве нынче используют unchecked-исключения, то есть те, которые не нужно объявлять в сигнатуре метода. Каждый, кто хоть немного писал на Джаве, знает — если использовать checked-исключения, каждый метод обрастает ими, как дно корабля — морским желудем. Эта зараза ползет по коду: метод, который вызывает зараженный метод, вынужден либо унаследовать бороду исключений, либо заткнуть их все и кинуть unchecked-исключение.
С одной стороны теоретики заливают о контрактах и безопасности кода. А с другой стороны — практика, которая доказывает: это неудобно. Знаете шутку: в теории между теорией и практикой нет разницы, а на практике есть? Это как раз тот случай.
Перехватывать исключения имеет смысл в редких случаях, например:
-
поллинг. Бывает, вы посылаете сервису задание: вычисли то-то и положи результат в S3. После этого начинаете поллить файл. На этом шаге логично перехватывать
FileNotFoundили похожие ошибки — до определенного лимита, конечно. -
Запасной источник данных — иногда одни и те же данные можно добыть разными способами: из базы данных и временного хранилища. Если второе отказало, можно сходить в базу, перехватив исключение.
-
Троттлинг — если вы часто ходите в какой-то ресурс, он может сказать “умерь пыл”. На уровне кода это будет исключение
RateLimitExceptionилиHTTPErrorс кодом 429. Их можно поймать, поспать секунду и повторить. -
Ошибки ввода-вывода. Бывает, моргнула сеть, подвис роутер и скачать файл не удалось. Поможет перехват IOException и повтор операции. Только не забывайте, что все продвинутые клиенты учитывают сетевые сбои. Внутри у них своя логика повтора в случае IOException.
Еще один случай — когда нужно показать красивое сообщение об ошибке. Даже если было исключение, некрасиво вываливать стек-трейс (а то и вовсе небезопасно) — нужно завершить программу культурно. Программисты на Питоне, мотайте на ус: ваши программы часто валятся со стек-трейсами, стоит только ввести не те данные.
Иногда ошибки кидают намеренно — опять же, по нескольким причинам. Во-первых,
встроенная функция может вернуть null, однако это не имеет смысла. Я уже
приводил пример: не задана переменная среды: токен, пароль и так
далее. Продолжать без нее нет смысла. Поэтому вместо System/getenv вызывают
свою функцию get-env!, которая кинет исключение, если переменной нет.
В противном случае вы будете долбиться в сеть с заголовком X-Authentication:
aws-null-null, и ничего хорошего из этого не выйдет.
Другая причина — внести ясность в чужие исключения. Порой их сообщения лишены деталей и потому бесполезны. Например, “file not found” — какой файл? “Port is busy” — какой порт? “HTTP 404” — какой урл? Когда вызов один, разобраться еще можно. Но представьте, что у вас цикл, пул тредов — желаю счастливой отладки.
Поэтому такие исключения оборачивают своими, которые говорят: file
C:\windows\mustdie.text not found, HTTP 404: GET http://test.com, port 5432
is busy и так далее.
Поймать исключение и подавить его, не имея на то причины — один из худших паттернов в программировании. Недавно я добился того, чтобы один сервис отвечал ошибкой, если нет файла в S3. До меня было так: разработчик читал его из S3 и парсил JSON-библиотекой. Все это он оборачивал в try-catch с логикой: если пошло не так, записать в лог и вернуть nil. В Кложе nil ведет себя как пустая коллекция, поэтому на вычислениях это не сказывалось, просто они были пустыми.
В день было по 50.000 (прописью — пятьдесят тысяч) подобных ошибок. Все они были в логах. Кто-то хоть раз их читал? Разумеется, нет.
Проблема вскрылась лишь в тот момент, когда код слегка изменили. В результате
получалась мапа с нуллом в ключе: {nil ...}, и наш JSON-сериализатор падал:
мол, не знаю, что делать с null-ключом. А до этого проблем не было. Не падает —
значит, все хорошо.
Так вот, возвращаясь к первоначальному тезису. Перехватывая исключения, думайте про “план Б”. Действительно ли он у вас есть? Лично я сомневаюсь. “План Б” — это буквально три-четыре случая, а все остальное — трусость, неопытность, желание замести проблемы под ковер.
Крайне вероятно, что имеет место второе, а не первое.
Нашли ошибку? Выделите мышкой и нажмите Ctrl/⌘+Enter