Накапливание throws'ов -- нормально ли это?
От: 0K Ниоткуда  
Дата: 18.12.10 08:03
Оценка:
Такой вопрос. Нормально ли, если у метода будет штук 10 throws'ов? Причем не у единичного, а у многих методов.

Вроде в открытых библиотеках такого не встречал. А вот когда я пишу -- получается по 10-15 этих throws'ов на метод.

Что я делаю не так?

Вот, к примеру, обратился к серверу и распарсил его XML-ответ. Получилосся такой послужной список:

ParserConfigurationException
SAXException
IOException
XPathExpressionException
TransformerException


У меня есть выбор: либо этот весь список добавить в throw, либо перехватить исключения локально, добавить в лог-файл, а функция вернет null вместо ответа. Что лучше?

И вообще, как вы решаете проблему с накапливанием исключений? Стараетесь их как можно раньше обработать и вернуть null (или прочий код возврата)? Или заменяете на другое исключение, не проверяемое? Или все пихаете в throws и хрен с ним?

В общем, как правильно?
=сначала спроси у GPT=
Re: Накапливание throws'ов -- нормально ли это?
От: elmal  
Дата: 18.12.10 08:20
Оценка: 4 (1)
Здравствуйте, 0K, Вы писали:

0K>В общем, как правильно?

Лично я их в основном конверчу в наследники от RuntimeException в обработчике. В довольно редких случаях возвращаю значение по умолчанию, или какую нидь константу в случае ошибки, если это допустимо (а это допустимо, например, при форматировании даты — лучше пусть юзер увидит ошибку на месте даты, чем ошибка будет прокинута на самый верх). Checked конечно использую, но редко. Например в случае вебсервиса я явно говорю, какого типа исключения кидаются.
Re: Накапливание throws'ов -- нормально ли это?
От: Andrey_spb Россия  
Дата: 18.12.10 08:32
Оценка:
Здравствуйте, 0K, Вы писали:

0K>Такой вопрос. Нормально ли, если у метода будет штук 10 throws'ов? Причем не у единичного, а у многих методов.


0K>Вроде в открытых библиотеках такого не встречал. А вот когда я пишу -- получается по 10-15 этих throws'ов на метод.


Может быть в данном случаи проблема не в накапливании throws'ов? Если судить по описанию метода, то он выполняет несколько различных функции, отсюда и большое количество потенциальных ошибок, требующих обработки. Может быть стоит разделить данный метод на несколько более мелких, и при этом в каждом из них будет небольшое количество исключений,
а вызывающий их метод мог бы их обрабатывать и генерировать исключение, которое было бы более информативно в контексте его использования.
Re: Накапливание throws'ов -- нормально ли это?
От: Miroff Россия  
Дата: 18.12.10 14:44
Оценка: 6 (3) +5
Здравствуйте, 0K, Вы писали:

0K>В общем, как правильно?


Правильно не раскрывать детали имплементации в публичном контракте. Перечисленные исключения это детали реализации. Их нужно собрать, обработать и вернуть что-то более приемлимое с точки зрения контракта. Например так:

...
} catch (TimeoutException e) {
    throw new ExternalServerAPIException("External API call was failed because of timeout", e);
} catch (OtherExceptionFromTheList e) {
    throw new ExternalServerAPIException("External API call was failed", e);
}


Не обязательно это будет одно исключение, может быть целая иерархия.

Почему нельзя возвращать null. null не отвечает контракту функции и требуется либо вводить костыли типа аннотации @Nullable с последующей автоматической проверкой, либо ловить злые баги, когда в одном месте забыли проверку на null.

Почему нельзя бросать RuntimeException. RuntimeException не описывается в контракте. Соответственно автор клиентского кода может вообще не догадаться что метод что-то там кидает.

Короче, checked exceptions придумали не просто так. К сожалению мало кто умеет ими пользоваться.
Re[2]: Накапливание throws'ов -- нормально ли это?
От: 0K Ниоткуда  
Дата: 18.12.10 15:10
Оценка:
Здравствуйте, Andrey_spb, Вы писали:

A_>Может быть в данном случаи проблема не в накапливании throws'ов? Если судить по описанию метода, то он выполняет несколько различных функции, отсюда и большое количество потенциальных ошибок, требующих обработки. Может быть стоит разделить данный метод на несколько более мелких, и при этом в каждом из них будет небольшое количество исключений,

A_>а вызывающий их метод мог бы их обрабатывать и генерировать исключение, которое было бы более информативно в контексте его использования.

Внутри этот метод вызывает более мелкие. Т.е. он не перегружен логикой. И сам выполняет одну конкретную функцию, по этому убирать его не стоит...
=сначала спроси у GPT=
Re[2]: Накапливание throws'ов -- нормально ли это?
От: 0K Ниоткуда  
Дата: 18.12.10 15:14
Оценка:
Здравствуйте, Miroff, Вы писали:

M>Не обязательно это будет одно исключение, может быть целая иерархия.


Да интересный вариант. Нужно взять на заметку.

M>Почему нельзя возвращать null. null не отвечает контракту функции и требуется либо вводить костыли типа аннотации @Nullable с последующей автоматической проверкой, либо ловить злые баги, когда в одном месте забыли проверку на null.


Абсолютно согласен. Возврат null -- это почти то же самое, что возврат кода ошибки (устаревшая парадигма языка C).

M>Почему нельзя бросать RuntimeException. RuntimeException не описывается в контракте. Соответственно автор клиентского кода может вообще не догадаться что метод что-то там кидает.


Интересно.

M>Короче, checked exceptions придумали не просто так. К сожалению мало кто умеет ими пользоваться.


Я уже начал относится к ним как к злу. Особенно угнетает, что при переопределении метода нельзя добавить новых исключений. Почему нельзя добавить? Ведь я добавляю логику -- значит могут и новые исключения появится.
=сначала спроси у GPT=
Re: Накапливание throws'ов -- нормально ли это?
От: out-of-the-way США www.tehnoromantik.net
Дата: 18.12.10 15:27
Оценка:
Здравствуйте, 0K, Вы писали:

0K>Такой вопрос. Нормально ли, если у метода будет штук 10 throws'ов? Причем не у единичного, а у многих методов.


0K>Вроде в открытых библиотеках такого не встречал. А вот когда я пишу -- получается по 10-15 этих throws'ов на метод.


Нет не очень нормально. Exception — тип не явно возвращаемый методом, а метод — часть интерфейса, стало быть Exception тоже относится к интерфейсу, а интерфейс мы проектируем так что бы им было удобно пользоваться. Методом который выбрасываем много Exception по моему не очень удобно пользоваться .

Я делаю так(ну это отправная точка для проектирования): предположим есть метод работа которого может завершиться аварийно и у меня есть три варианта действий на этот случай для преодоления трёх разных причин, значит будет три Exception.
Программа — мысли спрессованные в код.
Re[3]: Накапливание throws'ов -- нормально ли это?
От: elmal  
Дата: 18.12.10 15:30
Оценка: +1
Здравствуйте, 0K, Вы писали:

0K>Я уже начал относится к ним как к злу. Особенно угнетает, что при переопределении метода нельзя добавить новых исключений. Почему нельзя добавить? Ведь я добавляю логику -- значит могут и новые исключения появится.

Вообще, checked это не зло. Это очень и очень хорошая фича, которую просто нужно правильно применять. Например когда пишешь библиотечные классы или какие то API, то наружу обязательно отдавать именно checked. Так как тем самым описывается контракт, и все пользователи этого API будут знать об исключениях, и никогда не забудут про обработку. checked — это просто указание клиенту о том, что такие ситуации следует обрабатывать, от них никто не застрахован. А вот в случаях, когда все крутится внутри определенного модуля или внутри приложения, от checked будет больше вреда, чем пользы. Собственно я делаю так — checked от внешних либ прокидываю как наследник от RuntimeException (он используется внутри модуля или приложения), а если у нас это определенный довольно сложный модуль со своим API, которым пользуются другие, то перед вызовом ловим все Exception и прокидываем еще раз строго задокументирыванный checked exception.
Re[3]: Накапливание throws'ов -- нормально ли это?
От: Аноним  
Дата: 19.12.10 10:31
Оценка:
0K>Я уже начал относится к ним как к злу. Особенно угнетает, что при переопределении метода нельзя добавить новых исключений. Почему нельзя добавить? Ведь я добавляю логику -- значит могут и новые исключения появится.
Судя по всему, вы плохо понимаете, что такое полиморфизм. Новые исключения — нарушение контракта, которое может привести к непредсказуемым последствиям у пользователей вашей реализации. Они-то имеют дело с интерфейсом и ожидают вполне определенного поведения. Почему нарушение контракта это плохо? Представьте себе, что вы обновили версию сторонней библиотеки и, при неизменном интерфейсе, она начала выбрасывать в определенных ситуациях (очень неожиданно для вас) новое исключение, которое вы не обрабатываете, и которое может попасть к конечному пользователю в обход всех ваших тестов. Ухудшение качества продукта, заметное пользователю, это, в конечном счете, ваши финансовые потери.
Re: Накапливание throws'ов -- нормально ли это?
От: Baudolino  
Дата: 19.12.10 10:45
Оценка: +2
В Java с незапамятных времен (J2SE 1.4) есть механизм под названием Exception chaining, которым можно и нужно пользоваться. У ошибки при движении от более простого и низкоуровневого метода (например, чтение из файла) к более сложному (например, обработка запроса клиента) появляются новые семантические значения (нет доступа к файлу -> невозможно загрузить объект -> сервис недоступен), которые нужно отражать в модели исключений (например, SecurityException -> ResourceException -> ServiceException). Т.о. самый правильный подход — на каждом уровне иметь одно или иерархию исключений, соответствующих смыслу происходящего на этом уровне.
На уровне файловой системы это IOException и потомки (FileNotFound etc), на уровне слоя доступа к данным это, скажем, ResourceException (ResourceAccess и т.п.), на уровне слоя бизнес-сервисов — ServiceException и потомки (ServiceAccess, InternalError, InvalidRequest etc). Клиента совершенно не волнуют подробности о том, в каком файле ваша реализация хранит данные, его интересует более высокоуровневая диагностика — была ли это ошибка доступа к службе, неправильный запрос или внутренний сбой.
Re[2]: Накапливание throws'ов -- нормально ли это?
От: vsb Казахстан  
Дата: 20.12.10 08:26
Оценка:
Здравствуйте, Baudolino, Вы писали:

B>В Java с незапамятных времен (J2SE 1.4) есть механизм под названием Exception chaining, которым можно и нужно пользоваться. У ошибки при движении от более простого и низкоуровневого метода (например, чтение из файла) к более сложному (например, обработка запроса клиента) появляются новые семантические значения (нет доступа к файлу -> невозможно загрузить объект -> сервис недоступен), которые нужно отражать в модели исключений (например, SecurityException -> ResourceException -> ServiceException). Т.о. самый правильный подход — на каждом уровне иметь одно или иерархию исключений, соответствующих смыслу происходящего на этом уровне.

B>На уровне файловой системы это IOException и потомки (FileNotFound etc), на уровне слоя доступа к данным это, скажем, ResourceException (ResourceAccess и т.п.), на уровне слоя бизнес-сервисов — ServiceException и потомки (ServiceAccess, InternalError, InvalidRequest etc). Клиента совершенно не волнуют подробности о том, в каком файле ваша реализация хранит данные, его интересует более высокоуровневая диагностика — была ли это ошибка доступа к службе, неправильный запрос или внутренний сбой.

То, как это выглядит в коде — ужасно и порождает кучу boilerplate кода.

Возможно эту идею оживил бы синтаксический сахар вроде

int getActiveUserCount() throws DALException if SQLException {
}


но так, как оно выглядит сейчас (тысячи try-catch-throw), оно очень тяжело применимо.
Re: Накапливание throws'ов -- нормально ли это?
От: Donz Россия http://donz-ru.livejournal.com
Дата: 20.12.10 09:14
Оценка:
Здравствуйте, 0K, Вы писали:

0K>Такой вопрос. Нормально ли, если у метода будет штук 10 throws'ов? Причем не у единичного, а у многих методов.

0K>Вроде в открытых библиотеках такого не встречал. А вот когда я пишу -- получается по 10-15 этих throws'ов на метод.

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

0K>У меня есть выбор: либо этот весь список добавить в throw, либо перехватить исключения локально, добавить в лог-файл, а функция вернет null вместо ответа. Что лучше?


Возвращать null — самое плохое, что можно придумать. Во-первых, не надо смешивать два разных подхода к сообщению об ошибке: коды возврата и исключения. В яве есть исключения, так что возвращаемое значение должно содержать только легальные данные, говорящие о корректном результате выполнения метода. Во-вторых, это первый шаг к получению NPE. И вполне вероятно, твой код начнет содержать дополнительное объявление throws NullPointerException, то есть, усугубишь то, от чего пытаешься избавиться.

0K>И вообще, как вы решаете проблему с накапливанием исключений? Стараетесь их как можно раньше обработать и вернуть null (или прочий код возврата)? Или заменяете на другое исключение, не проверяемое? Или все пихаете в throws и хрен с ним?


Библиотечные классы только прокидывают исключения далее наверх с дополнительными комментариями, описывающими, при каких условиях они были брошены, если это надо.
Обработка исключений только на самом верху. А там обычно пишется в лог, на консоль и в зависимости от исключения может выдаваться приличное уведомление пользователю.
Re[3]: Накапливание throws'ов -- нормально ли это?
От: Baudolino  
Дата: 20.12.10 09:50
Оценка:
vsb>То, как это выглядит в коде — ужасно и порождает кучу boilerplate кода.
Я бы не стал называть это boilerplate кодом. То, как вы обрабатываете ошибки, определяет качество вашей реализации.

vsb>Возможно эту идею оживил бы синтаксический сахар вроде

vsb>
vsb>int getActiveUserCount() throws DALException if SQLException {
vsb>}
vsb>

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

vsb>но так, как оно выглядит сейчас (тысячи try-catch-throw), оно очень тяжело применимо.

Не вижу ничего тяжелого.
Re[2]: Накапливание throws'ов -- нормально ли это?
От: Baudolino  
Дата: 20.12.10 09:54
Оценка:
Здравствуйте, Donz, Вы писали:

D>И вполне вероятно, твой код начнет содержать дополнительное объявление throws NullPointerException...

NPE не надо объявлять

D>Обработка исключений только на самом верху.

Наверное, всё же не всегда? Код может быть устойчивым к некоторым типам исключений.
Re[3]: Накапливание throws'ов -- нормально ли это?
От: Donz Россия http://donz-ru.livejournal.com
Дата: 20.12.10 10:19
Оценка:
Здравствуйте, Baudolino, Вы писали:

D>>И вполне вероятно, твой код начнет содержать дополнительное объявление throws NullPointerException...

B>NPE не надо объявлять

Я не про требования языка, а про правильных подход
Если код явно подразумевает возможность NPE (например, явное throw new NPE()), то желательно.

D>>Обработка исключений только на самом верху.

B>Наверное, всё же не всегда? Код может быть устойчивым к некоторым типам исключений.

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