В моем проекте не так много исключений.
Прямо сейчас (мы используем MVC) у меня есть try catch, охватывающий весь мой код:
Интересно, есть ли веская причина использовать блок try-catch как можно более конкретным способом, чем я могу или просто сохранить его вообще, как сейчас?
Ответ 2
Идея исключения заключается в том, что функция может сообщать об ошибке, не возвращая специальные значения. В старом PHP, единственный способ, которым функция могла сказать, что проблема была связана с возвратом некоторого специального значения, такого как false
или -1
. Это не нравится. Например, предположим, что я пишу вариант file_get_contents().
Типичное возвращаемое значение - это дескриптор, представляемый положительным целым числом. Однако есть две основные проблемы, с которыми я могу столкнуться: указанный вами файл не найден, или указанный вами файл не читается. Чтобы указать на ошибку, я мог бы вернуть отрицательное число - потому что дескрипторы положительны - это связано с конкретной причиной ошибки. Скажем, что -1 означает, что файла там нет, а -2 означает, что файл не читается.
Теперь у нас есть проблема, что -1 и -2 не имеют в виду ничего для кого-то, кто читает код. Чтобы исправить это, мы вводим глобальные константы FILE_NOT_FOUND и FILE_NOT_READABLE. Посмотрим на какой-то результирующий код.
<?php
define('FILE_NOT_FOUND', -1);
define('FILE_NOT_READABLE', -2);
function my_file_get_contents($file) {
// blah blah blah
}
$friendListFile = getDefaultFriendListFile();
$result = my_file_get_contents($friendListFile);
if ($result == FILE_NOT_FOUND) {
deleteFriendListFromMenu();
} elseif ($result == FILE_NOT_READABLE) {
alertUserAboutPermissionProblem();
} else {
useFriendList($result);
}
Имея разные коды ошибок, мы можем действовать в соответствии с тем, что действительно проблема. Эта функциональность хорошо и хорошо. Проблема заключается в том, как читается код.
$result
- это ужасное имя переменной. Имена переменных должны быть описательными и очевидными, например $friendListFile
. Реальное имя для $result
- это $fileContentsOrErrorCode
, которое не только слишком длинное, но и показывает, как мы перегружаем одну переменную двумя значениями. Вы никогда, никогда, не хотите, чтобы одни и те же данные означали две вещи. Нам нужны отдельные $errorCode
и $fileContents
!
Итак, как нам обойти эту проблему? Одно не-действительно-решение, используемое некоторыми библиотеками PHP, состоит в том, чтобы вернуть их my_file_get_contents()
-подобные функции false
, если они столкнулись с проблемой. Чтобы устранить проблему, на самом деле, мы называем my_file_get_contents_getError()
. Это почти работает.
define('FILE_OKAY', 0);
define('FILE_NOT_FOUND', -1);
define('FILE_NOT_READABLE', -2);
$my_file_get_contents_error = FILE_OKAY;
function my_file_get_contents_getError() {
// blah blah blah
}
function my_file_get_contents($file) {
global $my_file_get_contents_error;
// blah blah blah
// whoa, an error? return false and store the error code in
// $my_file_get_contents_error
// no error? set $my_file_get_contents_error to FILE_OKAY
}
$friendListFile = getDefaultFriendListFile();
$result = my_file_get_contents($friendListFile);
if (my_file_get_contents_getError() == FILE_NOT_FOUND) {
deleteFriendListFromMenu();
} elseif (my_file_get_contents_getError() == FILE_NOT_READABLE) {
alertUserAboutPermissionProblem();
} elseif (my_file_get_contents_getError() == FILE_OKAY) {
useFriendList($result);
} else {
die('I have no idea what happened. my_file_get_contents_getError() returns '
. my_file_get_contents_getError()
);
}
В качестве примечания, да, мы можем сделать гораздо лучшую работу, избегая глобальной переменной и других подобных битов. Рассмотрим это демонстрацию гаек и болтов.
Мы все еще не можем назвать $result
чем-нибудь лучше, чем $fileContentsOrFalseIfError
. Эта проблема не была исправлена.
Теперь я исправил одну проблему, которую вы, возможно, заметили в предыдущем примере. Что делать, если мы не покрываем все коды ошибок? Если программист решает, что должен быть код -3, который мы изначально не обнаружили! Мы могли бы проверить, была ли $result
строка, чтобы убедиться, что это не код ошибки, но мы не должны действительно заботиться о типах в PHP, правильно? Теперь, когда мы можем использовать второе возвращаемое значение из my_file_get_contents_getError()
, нет никакого смысла включать код успеха.
Появилась новая проблема, которая возникла. Исправить один и найти еще три? Новая проблема заключается в том, что можно сохранить только самый последний код ошибки. Это ужасно хрупко! Если что-то еще вызывает my_file_get_contents()
, прежде чем вы обратитесь к вашему коду ошибки, их код перезапишет ваш!
Gah, теперь нам нужно сохранить список функций, которые небезопасно для вызова, прежде чем вы обратитесь к возвращаемому значению из my_file_get_contents_getError(). Если вы этого не сделаете, вы должны придерживаться условного соглашения о кодировании, которое вы всегда вызываете my_file_get_contents_getError()
сразу после my_file_get_contents()
, чтобы сохранить код ошибки, который принадлежит вам, прежде чем он будет таинственным образом перезаписан.
Подождите! Почему бы нам просто не раздавать идентификаторы нашим абонентам? Чтобы использовать my_file_get_contents()
, вам нужно задать create_my_file_get_contents_handle()
для некоторого числа, которое будет рассогласовывать вас со всеми другими вызывающими. Теперь вы можете вызвать my_file_get_contents($myHandle, $myFile)
, а код ошибки можно сохранить в специальном месте только для вас. Теперь, когда вы вызываете my_file_get_contents_getError($myHandle)
, вы можете получить доступ к этому специальному месту, получить код ошибки, и никто не наступил на ваши пальцы.
Er, но если есть много звонящих, мы не хотим, чтобы вокруг нас были блокированы бесполезные коды ошибок. Мы попросили пользователей позвонить destroy_my_file_get_contents_handle($myHandle)
, когда они будут сделаны, чтобы мы могли освободить некоторую память.
Надеюсь, это все очень хорошо знакомы вам в старых PHP-мантрах.
Это все так безумно, просто сделайте это простым, пожалуйста!
Что это значит, если язык поддерживает лучший механизм для реагирования на ошибки? Очевидно, что попытка создать некоторое решение с существующими инструментами является запутанной, неприятной и подверженной ошибкам.
Введите исключения!
<?php
class FileNotFoundException extends Exception {}
class FileNotReadableException extends Exception {}
function my_file_get_contents($file) {
if (!is_file($file)) {
throw new FileNotFoundException($file);
} elseif (!is_readable($file)) {
throw new FileNotReadableException($file);
} else {
// blah blah blah
}
}
$friendListFile = getDefaultFriendListFile();
try {
$fileContents = my_file_get_contents($friendListFile);
useFriendList($fileContents);
} catch (FileNotFoundException $e) {
deleteFriendListFromMenu();
} catch (FileNotReadableException $e) {
alertUserAboutPermissionProblem();
}
Внезапно наши старые головные боли с особыми возвращаемыми значениями, ручками и условными обозначениями были излечены!
Теперь мы действительно можем переименовать $result
в $fileContents
. Если my_file_get_contents()
имеет проблему, назначение прерывается полностью, и мы переходим к соответствующему блоку catch. Только если нет ошибки, мы даже думаем о предоставлении $fileContents
значения или вызова useFriendList()
.
Мы больше не страдаем от нескольких звонящих, наступающих друг на друга. Каждый вызов my_file_get_contents() будет создавать собственные исключения, если возникает ошибка.
Нет проблем с памятью! Сборщик мусора с удовольствием очистит ненужные объекты исключений, не думая об этом. Используя устаревшую систему ручек, мы должны были помнить о том, чтобы вручную уничтожить ручку, чтобы она не скрывалась навсегда в памяти.
Есть много других преимуществ и черт для исключений. Я настоятельно рекомендую посмотреть на другие источники, чтобы узнать об этом. Особенно интересны то, как они пузыряют стек исполнения, пока какой-то вызывающий может их поймать. Также интересно, как вы можете поймать исключение, попытаться исправить эту проблему, а затем перестроить исключение, если не можете. Не забывайте, что исключения - это объекты! Этому можно добиться большой гибкости. Для исключений, которые никто не может уловить, посмотрите на обработчик исключений.
Мое намерение ответить на вопрос состояло в том, чтобы продемонстрировать, почему нам нужны исключения. Делая это, я надеюсь, что легко определить, какие проблемы мы можем решить с ними.
Ответ 4
Помните, что исключения относятся к исключительным случаям. Насколько я понимаю, это происходит, когда ошибка выходит из-под контроля. Например, недопустимые параметры передаются публичной функции API, деление на ноль, такие ситуации, как "потеря сетевого соединения", "файл не найден"... такие вещи.
Как правило, вы должны поймать исключения, которые вы знаете, как обрабатывать, например восстановление из ошибки, регистрировать ошибку и распространять ее и т.д. Если вы не знаете, как ее обрабатывать, лучше разрешить он не работает. В противном случае ваше приложение может находиться в состоянии ошибки, которое вам может не понадобиться.
Поэтому, отвечая на ваш вопрос, лучше быть как можно более конкретным, поскольку каждое исключение должно обрабатываться только в том случае, если вы знаете, что с ним делать (молчание - это плохая идея). Если не просто позволить исключению уведомить пользователя о том, что что-то пошло не так. Или, если вы хотите, поймайте исключение, чтобы зарегистрировать ошибку и восстановить ее.
Хорошее обсуждение здесь для С++, но применяются общие понятия. Я нашел java учебники по исключениям тоже очень хорошо.