На самом деле никто еще не обратился к части «передовой практики», поэтому я перейду к ней.
Да, вы должны определенно генерировать исключение в своем коде, когда что-то пойдет не так, и вы должны сделать это как можно раньше (чтобы вы ограничили код, который необходимо отлаживать, чтобы выяснить, что его вызывает) .
Код, который делает такие вещи, как return undef
для обозначения сбоя, не особенно надежен просто потому, что люди будут использовать его, не проверяя возвращаемое значение undef - это означает, что переменная, которую они предполагают, имеет что-то значимое, на самом деле может не иметь. Это приводит к сложным, трудно поддающимся отладке проблемам и даже неожиданным проблемам, возникающим позже в ранее работающем коде.
Более надежный подход - написать свой код так, чтобы он умирал, если что-то пойдет не так, а затем только если вам нужно восстановиться после этого сбоя, оберните все вызовы к нему в eval{ .. }
(или, лучше, try { .. } catch { .. }
из Try :: Tiny, как уже упоминалось). В большинстве случаев вызывающий код не может сделать ничего значимого для восстановления, поэтому вызывающий код в общем случае остается простым, и вы можете просто предположить, что получите обратно полезное значение. Если что-то пойдет не так, вы получите сообщение об ошибке из фактической части кода, которая завершилась ошибкой, вместо того, чтобы молча получить undef. Если ваш вызывающий код может что-то сделать для восстановления сбоев, он может организовать перехват исключений и делать все, что ему нужно.
Стоит прочитать о классах исключений, которые представляют собой структурированный способ отправки дополнительных информация для вызывающего кода, а также позволяет ему выбирать, какие исключения он хочет перехватить, а какие не может обработать. Вы, вероятно, не захотите использовать их повсюду в своем коде, но они полезны, когда у вас есть что-то сложное, которое может выходить из строя одинаково сложным образом, и вы хотите, чтобы сбои можно было исправить.
08.03.2010