Каковы наилучшие методы для перехвата и повторного выбрасывания исключений?
должны ли пойманные исключения быть повторно брошены непосредственно, или они должны быть обернуты вокруг нового исключения?
то есть, я должен сделать это:
try {
$connect = new CONNECT($db, $user, $password, $driver, $host);
} catch (Exception $e) {
throw $e;
}
или такой:
try {
$connect = new CONNECT($db, $user, $password, $driver, $host);
} catch (Exception $e) {
throw new Exception("Exception Message", 1, $e);
}
Если ваш ответ кинуть напрямую пожалуйста, предложите использовать исключения-цепочки, Я не в состоянии понять реальный сценарий мира, где мы используем цепочку исключений.
5 ответов:
вы не должны поймать исключение если вы не намереваетесь сделать что-то значимое.
" что-то значимое " может быть одним из них:
обработка исключения
наиболее очевидным значимым действием является обработка исключения, например, путем отображения сообщения об ошибке и прерывания операции:
try { $connect = new CONNECT($db, $user, $password, $driver, $host); } catch (Exception $e) { echo "Error while connecting to database!"; die; }ведение журнала или частичная очистка
иногда вы не знаете, как правильно обрабатывать исключение внутри определенного контекста; возможно, Вам не хватает информации о "большой картине", но вы хотите зарегистрировать сбой как можно ближе к точке, где это произошло. В этом случае вы можете поймать, войти и повторно бросить:
try { $connect = new CONNECT($db, $user, $password, $driver, $host); } catch (Exception $e) { logException($e); // does something throw $e; }связанный сценарий, где вы находитесь в правильном месте, чтобы выполнить некоторую очистку для неудачной операции, но не решить, как сбой должен быть обработан на верхнем уровне. В более ранних версиях PHP это будет реализовано как
$connect = new CONNECT($db, $user, $password, $driver, $host); try { $connect->insertSomeRecord(); } catch (Exception $e) { $connect->disconnect(); // we don't want to keep the connection open anymore throw $e; // but we also don't know how to respond to the failure }PHP 5.5 представил
finallyключевое слово, поэтому для сценариев очистки теперь есть другой способ подойти к этому. Если код очистки должен выполняться независимо от того, что произошло (т. е. как при ошибке, так и при успешном выполнении), теперь это можно сделать, прозрачно разрешив распространение любых брошенных исключений:$connect = new CONNECT($db, $user, $password, $driver, $host); try { $connect->insertSomeRecord(); } finally { $connect->disconnect(); // no matter what }абстракция ошибок (с цепочкой исключений)
третий случай, когда вы хотите логически сгруппировать много возможных сбоев под большим зонтиком. Пример для логической группировки:
class ComponentInitException extends Exception { // public constructors etc as in Exception } class Component { public function __construct() { try { $connect = new CONNECT($db, $user, $password, $driver, $host); } catch (Exception $e) { throw new ComponentInitException($e->getMessage(), $e->getCode(), $e); } } }в этом случае, вы не хотите, чтобы пользователи
Componentчтобы знать, что он реализован с использованием подключения к базе данных (возможно, вы хотите сохранить свои параметры открытыми и использовать файловое хранилище в будущем). Так что ваша спецификация дляComponentсказал бы, что " в случае сбоя инициализации,ComponentInitExceptionбудет брошен". Это позволяет потребителямComponentчтобы поймать исключения ожидаемого типа в то время как также разрешение отладочного кода для доступа ко всем (зависящим от реализации) деталям.предоставление более богатого контекста (с цепочкой исключений)
наконец, есть случаи, когда вы можете предоставить больше контекста для исключения. В этом случае имеет смысл обернуть исключение в другое, которое содержит больше информации о том, что вы пытались сделать, когда произошла ошибка. Например:
class FileOperation { public static function copyFiles() { try { $copier = new FileCopier(); // the constructor may throw // this may throw if the files do no not exist $copier->ensureSourceFilesExist(); // this may throw if the directory cannot be created $copier->createTargetDirectory(); // this may throw if copying a file fails $copier->performCopy(); } catch (Exception $e) { throw new Exception("Could not perform copy operation.", 0, $e); } } }этот случай аналогичен приведенному выше (и пример, вероятно, не самый лучший, который можно было бы придумать), но он иллюстрирует точку предоставления большего контекста: если возникает исключение, оно говорит нам, что копия файла не удалась. Но почему разве это плохо? Эта информация представлена в обернутых исключениях (из которых может быть более одного уровня, если пример был намного сложнее).
значение этого иллюстрируется, если вы думаете о сценарии, где, например, создание
Ну, это все о сохранении абстракции. Поэтому я бы предложил использовать цепочку исключений для прямого броска. Что касается того, почему, позвольте мне объяснить концепцию дырявых абстракций
допустим, вы строите модель. Предполагается, что модель абстрагирует все сохраняемость и проверку данных от остальной части приложения. Итак, что же происходит, когда вы получаете ошибку базы данных? Если вы перестроить
DatabaseQueryException, ты протекающие абстракции. Взять в толк подумайте об абстракции на секунду. Тебе все равно как модель хранит данные, только то, что он делает. Точно так же вам все равно, что пошло не так в базовых системах модели, просто вы знаете, что что-то пошло не так, и примерно то, что пошло не так.таким образом, переосмысливая DatabaseQueryException, вы просачиваете абстракцию и требуете, чтобы вызывающий код понимал семантику того, что происходит под моделью. Вместо этого создайте общий
ModelStorageException, и обернуть пойманныйDatabaseQueryExceptionвнутри этого. Таким образом, ваш вызывающий код все еще может попытаться справиться с ошибкой семантически, но это не имеет значения, лежащая в основе технология модели, так как вы только подвергаете ошибки из этого уровня абстракции. Еще лучше, так как вы обернули исключение, если оно пузырится полностью и должно быть зарегистрировано, вы можете проследить до корневого исключения, брошенного (пройдитесь по цепочке), чтобы у вас все еще была вся отладочная информация, которая вам нужно!не просто поймать и перестроить то же исключение, если вам не нужно сделать некоторые пост-обработки. Но блок, как
} catch (Exception $e) { throw $e; }- это бессмысленно. Но вы можете повторно обернуть исключения для некоторого значительного усиления абстракции.
ИМХО, ловя исключение, чтобы просто перестроить его бесполезно. В этом случае просто не поймайте его, и пусть ранее вызванные методы обрабатывают его (aka методы, которые являются "верхними" в стеке вызовов).
если вы перестроите его, цепочка пойманного исключения в новый, который вы бросите, безусловно, хорошая практика, так как она будет хранить информацию, содержащуюся в пойманном исключении. Однако переосмысление его полезно только в том случае, если вы добавить некоторую информацию или обрабатывать что-то для пойманного исключения, может быть, это какой-то контекст, значения, ведение журнала, освобождение ресурсов, что угодно.
способ добавить некоторую информацию для расширения
Exceptionкласс, чтобы иметь исключения, какNullParameterException,DatabaseExceptionи т. д. Более того, это позволяет разработчику ловить только некоторые исключения, которые он может обрабатывать. Например, можно поймать толькоDatabaseExceptionи попытаться решить, что вызвалоException, Как подключиться к базе данных.
вы обычно думаете об этом так.
класс может выдавать много типов исключений, которые не будут совпадать. Таким образом, вы создаете класс исключения для этого класса или типа класса и бросаете его.
таким образом, код, который использует класс, должен поймать только один тип исключения.
вы должны взглянуть на исключение лучшие практики в PHP 5.3
обработка исключений в PHP не является новой функцией с какой-либо натяжкой. В следующей ссылке вы увидите две новые функции в PHP 5.3, основанные на исключениях. Первый-это вложенные исключения, а второй-новый набор типов исключений, предлагаемых расширением SPL (которое теперь является основным расширением среды выполнения PHP). Обе они нашли свое место в книге лучший практики и заслуживают детального изучения.
http://ralphschindler.com/2010/09/15/exception-best-practices-in-php-5-3
Comments