Главная » Java, Советы » Инициируйте исключения, соответствующие абстракции

0

 

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

Во избежание этой проблемы верхние уровни приложения должны перехватывать исключения нижних уровней и, в свою очередь, инициировать исключения, которые можно объяснить в терминах абстракции верхнего уровня. Описываемая идиома, которую мы называем трансляцией исключении. (exception translation), выглядит следующим образом:

 

// Трансляция исключения

try {

// Использование абстракции нижнего уровня

// для выполнения наших указаний

} catch(LowerLevelException е) {

throw new HigherLevelException( … );  }

 

Приведем конкретный пример трансляции исключения, взятый из класса AbstractSequentialList, который представляет собой скелетную реализацию (статья 16) интерфейса List. В этом примере трансляция исключения продиктована спецификацией метода get в интерфейсе List:

 

/**

* Возвращает элемент, находящийся в указанной позиции

* в заданном списке.

* @throws IndexOutOfBoundsException, если индекс находится

* за пределами диапазона (index < 0 II index >= size()).

*/

public Object get(int index) {

                ListIterator i = listItеrаtor(index);                       

try {

return i. next();

catch (NoSuchElementException е) {

throw new IndexOutOfBoundsException("Index: " + index); }

}

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

 

// Сцепление исключений

try {

// Использование абстракции нижнего уровня

// для выполнения наших указаний

} catch(LowerLevelException е) {

throw new H1gherLevelException(e);  }

в версии 1.4 сцепление исключений поддерживается классом Throwable. В этой и последующих версиях можно использовать преимущества такой поддержки, связав ваш конструктор исключения верхнего уровня с конструктором Throwable(Throwable):

 

// Сцепление исключений в версии 1.4

HigherLevelException(Throwable t) {

super(t);   }

 

 

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

// Сцепление исключений в версии, предшествующей 1.4

private Throwable cause;

НighегLеvеlЕхсерtiоп(Тhгоwаblе t) {

cause = t;  }

public Throwable getCause() {

return cause;   }

Дав методу доступа название getCause и применив указанную декларацию, вы получите гарантию того, что ваше исключение будет взаимодействовать с механизмом сцепления исключений для данной платформы так, как если бы вы использовали исключение в версии 1.4. Это дает то преимущество, что в исключение верхнего уровня стандартным образом будет интегрирована трассировка стека для исключения нижнего уровня. Кроме того, это позволяет задействовать стандартные средства отладки для доступа к исключению нижнего уровня.

Хотя трансляция исключений лучше, чем бессмысленная передача исключений с нижних уровней, злоупотреблять ею не следует. Самый хороши" способ обработки исключений нижнего уровня – полностью исключить их возможность. Для этого перед выбором метода нижнего уровня необходимо убедиться в том, что он будет выполнен успешно, иногда добиться этого можно путем явной проверки аргументов метода верхнего уровня перед их передачей на нижний уровень.

Если предупредить появление исключений на нижних уровнях невозможно, то лучшее решение состоит в том, чтобы верхний уровень молча обрабатывал эти исключения, изолируя клиента от проблем нижнего уровня. В таких условиях чаще всего достаточно протоколировать исключения, используя какой-либо механизм регистрации, например java,util,logging, появившийся в версии 1.4. Это дает возможность администратору исследовать возникшую проблему и в то же время изолирует от нее программный код клиента и конечного пользователя.

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

 

Источник: Джошуа Блох, Java TM Эффективное программирование, Издательство «Лори»

По теме:

  • Комментарии