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

>  } finally {

>    …

>  }

Класс LocalPort представляет собой простую обертку, которая перехватывает и преобразует исключения, инициированные классом ACMEPort:

>public class LocalPort {

>  private ACMEPort innerPort;


>  public LocalPort(int portNumber) {

>    innerPort = new ACMEPort(portNumber);

>  }


>  public void open() {

>    try {

>      innerPort.open();

>    } catch (DeviceResponseException e) {

>      throw new PortDeviceFailure(e);

>    } catch (ATM1212UnlockedException e) {

>      throw new PortDeviceFailure(e);

>    } catch (GMXError e) {

>      throw new PortDeviceFailure(e);

>    }

>  }

>  …

>}

Обертки — вроде той, которую мы определили для ACMEPort, — бывают очень полезными. Более того, инкапсуляция вызовов сторонних API принадлежит к числу стандартных приемов. Создавая обертку для стороннего вызова, вы сокращаете до минимума зависимость от него в своем коде: в будущем вы можете переключиться на другую библиотеку без сколько-нибудь заметных проблем. Обертки также упрощают имитацию сторонних вызовов в ходе тестирования кода.

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

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

Определите нормальный путь выполнения


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

Рассмотрим конкретный пример. В следующем, довольно неуклюжем фрагменте суммируются командировочные расходы на питание:

>try {

>  MealExpenses expenses = expenseReportDAO.getMeals(employee.getID());

>  m_total += expenses.getTotal();

>} catch(MealExpensesNotFound e) {

>  m_total += getMealPerDiem();