Как стать автором
Поиск
Написать публикацию
Обновить

Исключения это инструмент отладки

Контекст: PHP.

Чего не хватает в этом коде?
$hf = fopen('file.txt', 'w');
fwrite($hf, 'text');


Правильно, проверок на ошибки. Программист должен смотреть в обе стороны, даже когда он переходит дорогу с односторонним движением.

Проверка в ходе выполнения:
$hf = fopen('file.txt', 'w');
if (!$hf)
{
print "Can\'t open file";
}
else
{
if (!fwrite($hf, 'text'))
{
print "Can\'t write to file";
}
}


Проверка через try-catch:
try
{
$hf = fopen('file.txt', 'w');
fwrite($hf, 'text');
}
catch (Exception $exception)
{
print 'File handler error ['.$exception->getCode().'] '.$exception->getMessage();
}


Можем ли мы забыть сделать проверку в ходе выполнения?
Да.
Последствия: перехватим ошибки в error_handler, запишем в лог вместе с трейсом и продолжим выполнение.

Можем ли мы забыть написать try-catch?
Легко.
Последствия: вылетим из контекста выполнения, потеряем объект, в котором происходило выполнение и, возможно, очень важные данные хранились в его свойствах.

Пример

Есть фронт-контроллер, в котором ловятся все не пойманные исключения.
Фронт-контроллер роутит запрос контроллеру, в запросе передаётся идентификатор и контроллер считывает его. Мы не можем вернуть ответ обычным выводом print или echo, мы должны сделать запрос к внешнему API и в этом запросе мы указываем тот самый идентификатор (ID клиента).

Контроллер передаёт запрос в модель, модель производит транзакцию, о результате которой нужно сообщить клиенту, отдаёт результат контроллеру. Контроллер, чтобы оформить этот результат в необходимом формате (XML) передаёт этот результат во View, View генерирует оформленние ответа и… в коде шаблона обнаружилась синтаксическая ошибка, View выбрасывает исключение, оно ловится во фронт-контроллере и…
Мы вылетели из нашего контроллера.

Можно пророутить в fallback-контроллер, но кто сказал, что он обязан знать о том идентификаторе, который нужно спарсить? Кто сказал, что fallback-контроллер вообще в курсе про то, что ответ нужен не в stdout, а в виде запроса к API?
Ничего такого fallback-контроллер знать не может, иначе будет дублирование логики. Fallback-контроллер может вывести красивую страницу «Извините, ошибка», это его единственная задача.

Исправляем

Что нужно сделать, чтобы предотвратить это?
Нужно сделать try-catch в контроллере.
Чем это отличается от проверки на ошибки в ходе выполнения?
Ничем.
Станет ли код удобочитаемее, по сравнению с проверкой на ошибки?
Нет. Код даже станет менее логичен.
Как мы узнали о том, что в контроллере нужно поймать исключение?
При ошибке.

Исправление ошибок — задача этапа отладки.
Вывод: исключения это инструмент отладки. В отлаженной программе все случаи ошибок должны быть предусмотрены и проверены в ходе выполнения, чтобы избежать вылетов из контекста выполнения.

Рассуждения

Выбрасывание исключения может нарушить ход выполнения алгоритмов сразу нескольких объектов.
А ведь вполне возможна ситуация, когда одному из этих алгоритмов было не особо важно успешное выполнение той части кода, где вылетело исключение, но его выполнение прервалось и это привело к гораздо более серьёзной ошибке.

Выбрасывая исключение, мы можем лишь надеятся на то, что вызывающий код будет его ловить.
Вызывая функцию без try-catch (когда результат выполнения не критичен), мы можем лишь надеятся на то, что функция (или те функции, которые она вызовет) не выбросит исключение, прервав ход выполнения.
Это нельзя назвать надёжным алгоритмом.

Даже если у нас вылетело исключение с сообщением «не обнаружен жёсткий диск», мы можем перехватить эту ошибку, переместить логгирование с диска в память и отправить сообщение администратору, вызвав специальный сторонний API.
Любое исключение — недоработка проверки хода выполнения.
Теги:
Хабы:
Данная статья не подлежит комментированию, поскольку её автор ещё не является полноправным участником сообщества. Вы сможете связаться с автором только после того, как он получит приглашение от кого-либо из участников сообщества. До этого момента его username будет скрыт псевдонимом.