Вот моя основная философия (и другие случайные мысли) об исключениях.
Когда вы вызываете метод, у вас есть определенные надежды на то, что метод будет выполнять. Формально эти ожидания называются пост-условиями. Метод должен вызывать исключение всякий раз, когда он не отвечает своим постусловиям.
Чтобы эффективно использовать эту стратегию, это подразумевает, что вы должны иметь небольшое представление о дизайне по контракту и значение pre- и пост-условия. Я думаю, что это хорошо знать.
Вот некоторые конкретные примеры. Метод save
модели Rails:
model.save!
-- post-condition: The model object is saved.
Если модель по какой-то причине не сохраняется, то исключение должно быть возбуждено, потому что пост-условие не выполняется.
model.save
-- post-condition: (the model is saved && result == true) ||
(the model is not saved && result == false)
Если save
фактически не сохраняет, то возвращаемый результат будет false
, но пост-условие все еще выполняется, следовательно, исключение не будет.
Мне интересно, что save!
метод имеет гораздо более простое пост-условие.
На тему спасения исключений, я думаю, что приложение должно иметь стратегические точки, где исключены исключения. По большей части мало необходимости в спасении/повторении. Единственный раз, когда вы хотели бы спасти и реконструировать, - это когда у вас есть работа на полпути, и вы хотите отменить что-то, чтобы избежать частично полного состояния. Ваши стратегические точки спасения следует выбирать осторожно, чтобы программа могла продолжить работу, даже если текущая операция не удалась. Программы обработки транзакций должны просто перейти к следующей транзакции. Приложение Rails должно восстановиться и быть готовым к обработке следующего HTTP-запроса.
Большинство обработчиков исключений должны быть общими. Поскольку исключения указывают на отказ какого-либо типа, тогда обработчик должен только принять решение о том, что делать в случае сбоя. Подробные операции восстановления для очень специфических исключений обычно обескуражены, если обработчик не очень близок (график вызовов мудрый) до точки исключения.
Исключения не должны использоваться для управления потоком, для этого используйте throw/catch
. Это резервирует исключения для истинных условий отказа.
(В стороне, потому что я использую исключения для указания сбоев, я почти всегда использую ключевое слово fail
а не ключевое слово raise
в Ruby. Fail
and raise
- синонимы, поэтому нет никакой разницы, кроме того, что fail
более четко указывает, что метод не прошел. Единственное время, когда я использую raise
- это когда я улавливаю исключение и повторно его поднимаю, потому что здесь я не сработал, но явно и целенаправленно создаю исключение. Это стилистическая проблема, за которой я следую, но я сомневаюсь, что это делают многие другие люди).
Там у вас это есть, довольно бессвязные воспоминания о моих мыслях об исключениях.