Как стать автором
Обновить

Как правильно использовать исключения

Время на прочтение3 мин
Количество просмотров15K
Жаль, если ваше изучение концепции исключений закончится чтением соответствующего раздела официальной документации по вашему языку.

Изучив синтаксис конструкции try{...}catch(Exception $e){...}, узнав о возможности создавать собственные классы исключений, наследуя их от класса Exception и поверхностно осознав мощь оказавшегося в ваших руках механизма, дальше вы можете пойти по одному из двух путей:
  • Начать тут же использовать исключения. Скажем, прикручивать их к системе, в которой они никогда не использовались. Или приколачивать их к проекту, в котором ООП и не пахнет. Или, что самое ужасное, пытаться использовать их повсеместно, особенно там, где это не нужно.
  • Попытаться понять, где их применять, как делать это правильно, и зачем они нужны.


1. Нужно уметь чётко отличать в программе исключительную ситуацию от рядовой ошибки, например: неправильно введённые логин или пароль — это не исключительная ситуация! Это ошибка пользователя, а не программы, и она может происходить оченьчасто. С другой стороны, сбой в работе функции mysql_connect() из-за недоступности сервера БД — это исключительная ситуация, исключение нужно бросать! Подробнее об этом дальше.

2. Методы класса не должны перехватывать исключения, сгенерированные другими методами этого же класса. Библиотека вообще ничего не должна знать о том, что делать в случае возникновения исключительной ситуации в её методах, так как в зависимости от системы, где она используется, это поведение может сильно различаться. Её дело — сгенерировать исключение. К примеру, если файла, требующегося для корректной работы всего вашего класса, не существует, вы не должны с этим разбираться. Это дело вышестоящих уровней приложения.

С другой стороны, перехватывать исключения, брошенные нижестоящими уровнями, Вы вполне можете (если хотите). Если не хотите, они будут переданы по стеку в ближайший catch{} (точнее в catch{}, соответствующий ближайшему try{} ). Допустим, у вас есть класс-обёртка для работы с БД, с методами типа connect(), query() и так далее. Разумеется, если к серверу подключиться не удаётся, необходимо сгенерировать исключение в методе connect().

Однако перехватывать его не должен ни сам метод connect(), ни даже метод query(), который мог вызвать connect() автоматически! Данное исключение должно быть перехвачено на вышестоящем уровне, который работает с методами этого класса, и там должно быть принято решение — попробовать подключиться к другому серверу, использовать другой тип источника данных, просто вывести на экран ошибку, или передать исключение ещё выше (в разных системах по-разному, пускай сами решают, что делать). Надеюсь, мысль понятна.

3. В крупном приложении необходимо использовать свои собственные классы исключений (унаследованные от встроенного Exception). Это позволяет выстроить иерархию классов ошибок и разделять их по важности, типу и так далее. Допустим, у нас есть класс Application_Exception:

class Application_Exception extends Exception{...}
и несколько его потомков:


class Logic_Exception extends Application_Exception{...}
-- логические ошибки, например, нарушение связности таблиц в БД или уникальности ключей.


class Data_Access_Exception extends Application_Exception{...}
-- ошибки доступа к необходимым данным, вроде отсутствия файла, или устройства, или связи для доступа к какому-то источнику.


class Security_Exception extends Application_Exception{...}
-- ошибки с безопасностью, например человек присылает куку, которой у него просто не может быть ;)


Все эти типы ошибок должны обрабатываться по-разному, например, про ошибки с безопасностью не стоит кричать на весь интернет, а вот в случае отказа сервера БД можно вывести интеллигентную табличку с извинениями (в общем, на вкус и цвет).

4. Для получения максимального количества информации об ошибке, используйте возможности встроенного класса Exception, не изобретайте велосипедов! Если вам недостаточно имени файла и строки, где было брошено исключение, к вашим услугам методы getTrace() и getPrevious(), которые уж точно выложат перед вами всю «картину» произошедшего (подробнее в документации).

В этой заметке я попытался изложить основные теоретические положения, которые считаю наиболее важными при использовании исключений. Оригинал записи находится в моём блоге.
Желаю удачи!
Теги:
Хабы:
Всего голосов 122: ↑109 и ↓13+96
Комментарии98

Публикации

Истории

Работа

PHP программист
96 вакансий

Ближайшие события

7 – 8 ноября
Конференция byteoilgas_conf 2024
МоскваОнлайн
7 – 8 ноября
Конференция «Матемаркетинг»
МоскваОнлайн
15 – 16 ноября
IT-конференция Merge Skolkovo
Москва
22 – 24 ноября
Хакатон «AgroCode Hack Genetics'24»
Онлайн
28 ноября
Конференция «TechRec: ITHR CAMPUS»
МоскваОнлайн
25 – 26 апреля
IT-конференция Merge Tatarstan 2025
Казань