Чистый код. Создание, анализ и рефакторинг (Мартин) - страница 86

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

Передавайте контекст с исключениями

Каждое исключение, инициируемое в программе, должно содержать достаточно контекстной информации для определения источника и местонахождения ошибки. В Java из любого исключения можно получить данные трассировки стека; однако по трассировке невозможно узнать, с какой целью выполнялась операция, завершившаяся неудачей.

Создавайте содержательные сообщения об ошибках и передавайте их со своими исключениями. Включайте в них сведения о сбойной операции и типе сбоя. Если в приложении ведется журнал, передайте информацию, достаточную для регистрации ошибки из секции catch.

Определяйте классы исключений в контексте потребностей вызывающей стороны

Существует много способов классификации ошибок. Например, ошибки можно классифицировать по источнику, то есть по компоненту, в котором они произошли. Также возможна классификация по типу: сбои устройств, сетевые сбои, ошибки программирования и т.д. Однако при определении классов исключений в приложениях думать необходимо прежде всего о том, как они будут перехватываться.

Рассмотрим пример неудачной классификации исключений. Далее приводится конструкция try-catch-finally для сторонней библиотечной функции. Она учитывает все исключения, которые могут быть инициированы при вызовах:

>ACMEPort port = new ACMEPort(12);


>try {

>  port.open();

>} catch (DeviceResponseException e) {

>  reportPortError(e);

>  logger.log("Device response exception", e);

>} catch (ATM1212UnlockedException e) {

>  reportPortError(e);

>  logger.log("Unlock exception", e);

>} catch (GMXError e) {

>  reportPortError(e);

>  logger.log("Device response exception");

>} finally {

>  …

>}

Конструкция содержит множество повторений, и это неудивительно. В большинстве ситуаций при обработке исключений выполняются относительно стандартные действия, не зависящие от их реальной причины. Мы должны сохранить ошибку и убедиться в том, что работа программы может быть продолжена. В этом случае, поскольку выполняемая работа остается более или менее постоянной независимо от исключения, код можно существенно упростить — для этого мы создаем «обертку» для вызываемой функции API и обеспечиваем возвращение стандартного типа исключения:

>  LocalPort port = new LocalPort(12);

>  try {

>    port.open();

>  } catch (PortDeviceFailure e) {

>    reportError(e);

>    logger.log(e.getMessage(), e);