> } 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();