Pull to refresh

Умные эксепшены

Reading time 2 min
Views 5K
Недавно ковырял паттерны проектирования и попутно пытался переделать один проект на эксепшены. Из этого коктейля родилось несколько мыслей, которыми хочется поделиться с общественностью.

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

Как правило эксепшены используют как своего рода контейнер содержащий информацию об исключительной ситуации. Одна часть кода генерит исключение, заключив в него инфомацию о событии, а другая часть кода ловит исключение и пытатся его обработать. Это обычный метод работы с исключениями. Но кто сказал, что это всё на что они способны?

Давайте разберемся.

Обычно эксепшены используют следующим образом:

class a
{
  public function mytest()
  {
     try
     {
        code_which_throw_exception();
     }
     catch(myExceptionType1 $e)
     {
        myException1Catcher();
     }
     catch(myExceptionType2 $e)
     {
        myException1Catcher();
     }
     ...

  }
}


Но почему, скажите, наши исключения — «молчащий», пассивный объект? Давайте дадим ему немного больше свободы! Раз это объект, то он может инкапсулировать логику обработки.

Давайте добавим в наши исключения метод catchException() в который спрячем логику myException1Catcher() и myException2Catcher() соответственно. Этому методу мы можем, например, передавать вызывающий объект в качестве параметра.

Тогда наш код превратится в следующий:

class a
{
  public function mytest()
  {
     try
     {
        code_witch_throw_exception();
     }
     catch(myException $e)
     {
        $e->catchException($this);
     }
  }
}


Как видите, код вызывающего класса немного сократился. Стал ли он лучше? Это зависит от ситуации.

Когда данный метод может быть полезен


Если в каком-то месте системы может генериться достаточно большое количество исключений разного типа, то метод может оказаться довольно удобен.

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

Какие преимущества дает данный метод


Объекту, обрабатывающему исключение, не нужно знать его точный тип, ведь все исключения обрабатываются одинаково. Для правильной работы достаточно совместимости на уровне интерфейса.

Эксепшену, в свою очередь, не нужно знать, какой именно объект его поймал: получив ссылку на $this, он может вызывать последовательность методов поймавшего объекта и, соответственно, тоже требует совместимости только на уровне интерфейсов.

Кроме того, появляется возможность внутри обработчика исключения сохранять в эксепшене дополнительные данные, которые будут перемещаться с ним и, например, вести статистику или даже менять алгоритм действий.
Tags:
Hubs:
+1
Comments 19
Comments Comments 19

Articles