Модераторы: Partizan, gambit
  

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> throw or not throw, Практика использования исключений 
:(
    Опции темы
nikitao
Дата 8.6.2011, 23:02 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Кот-программист
***


Профиль
Группа: Завсегдатай
Сообщений: 1206
Регистрация: 30.8.2005
Где: Спб

Репутация: 4
Всего: 26



Добрый день.
Хочется разобраться в этой теме. Как я понял единства и стандарта в использовании исключений не наблюдается.
К примеру Рихтер и Макконнелл 2 ортогональных мнения высказывают в своих книгах ( как мне показалось).
Первый говорит - используйте их как можно больше и лучше. Все минусы с лихвой окупятся их плюсами.
Второй -  "Генерируйте исключения , только в исключительных ситуациях".

В Рихтере приводится пример ( пишу по памяти ) :
Есть клиент банка , у него есчь счет. Он хочет перевести деньги на другой счет. Пишем :
Код

Account from=new Account(бла бла бла);
Account to=new Account(бла бла);
from.Transfer(to,500);

И дальше метод Transfer должен генирировать исключения , если не достаточно денег на счету , или доступ к одному из аккаунтов запрещен или просто банковская система повисла и т д. Но это все идет в разрез с тем , что говорит Макконнелл( на мой взгляд ) , потому что какие ж это исключительные ситуации - я вполне могу представить , что денег на счету меньше , чем их хотят перевести. Типичная, а не исключительная ситуация...

Очень популярна фраза "Генерируйте исключения , только в исключительных ситуациях". И дальше автор приводит какой то травильный пример. Создается впечатление , что понятие исключительная ситуация у программиста впитывается с молоком матери smile 
Я серьезно , для меня так самое сложное понять для себя - что такое исключительная ситуация.
Просто напросто, если я подумал о какой то ситуации , что она может возникнуть - то она уже не исключительная , раз я ее смог предусмотреть.

Бывают случаи , когда действительно понятно , когда надо их генерировать. Там деление на 0 или отсутствие доступа к файлу. Тут понятно, что метод ничего не может поделать, поэтому лучше всего кинуть исключение.
Но меня же интересуют совершенно другие ситуации. Приведу пример (уже из моей практики ) 

Есть какой то класс , которые отправляет сообщения на сервер

Код

ServiceClient client=new ServiceClient();
client.SendPost(message);


Это сообщение может не отправиться с 10 причин : доступ этому аккаунту запрещен , внутренняя ошибка сервера , сервер запросил дополнительную авторизация , сообщение не проходит по каким то фильтрам и т д.
Считать это исключительными ситуацией или нет ? 
Это ошибка - безусловна да.
Исключительная ситуация - ну у меня язык не поворачивается это назвать исключительной ситуацией , потому что они возникают чаще , чем не исключительные. (Ну вот там такая специфика работы)
Тогда здесь лучше коды возврата использовать ( через енумы ) или как то еще ?
Проблема еще в том , что я то пишу библиотеку и я не знаю , как ей будет пользоваться ее пользователь. Как ему будет удобнее ( через енумы или через коды возврата ) 

Или вот еще пример , где не понятно :
Код

Stream.ReadByte();

Возвращает -1 , если достигнут конец потока. С чего ? Почему он возвращает -1 , а не генерирует исключение ?
По этой логике при делении на 0 - надо возвращать бесконечность или минус бесконечность.

Хочется выработать какой то внутренний стандарт относительно этого вопроса.

Спасибо.


--------------------
Жизнь - печальная штука.
PM MAIL ICQ Skype GTalk   Вверх
wester
Дата 8.6.2011, 23:45 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 675
Регистрация: 27.2.2009

Репутация: 4
Всего: 13



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

Цитата

Хочется выработать какой то внутренний стандарт относительно этого вопроса.

это все вырабатывается собственным опытом. 

везде писал "лично" ибо все зависит от архитектуры проекта
PM MAIL   Вверх
nikitao
Дата 8.6.2011, 23:55 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Кот-программист
***


Профиль
Группа: Завсегдатай
Сообщений: 1206
Регистрация: 30.8.2005
Где: Спб

Репутация: 4
Всего: 26



Ну вот на самом деле хочется как раз узнать кому что их личный опыт подсказывает.

wester, я правильно понял , что вы обычно исключения вообще избегаете ? 
А если в ф-цию передаются неверные параметры ( там null к примеру в кач-ве одного из параметров) вы тоже код возвращаете ?
Насчет архитектуры это интересно. Но если предположить , что я пишу библиотеку , то у меня нет всего приложение и значит всей архитектуры в целом. Что тогда делать ? Как считаете ?

Это сообщение отредактировал(а) nikitao - 8.6.2011, 23:58


--------------------
Жизнь - печальная штука.
PM MAIL ICQ Skype GTalk   Вверх
ДобренькийПапаша
Дата 9.6.2011, 07:30 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1278
Регистрация: 14.1.2006
Где: г.Москва

Репутация: нет
Всего: 7



nikitao, спасибо за тему. Тоже очень хотелось бы узнать мнение более опытных коллег. Особенно в случае, когда архитектура никакая не ясна. С чётким проектированием и разработкой архитектуры начинаются далеко не все проекты и не во всех компаниях smile


--------------------
Меня зовут Себастьян Парейра, торговец чёрным деревом.
PM MAIL   Вверх
Stolzen
Дата 9.6.2011, 15:06 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1041
Регистрация: 17.10.2005

Репутация: нет
Всего: 48



Я думаю, прежде всего следуюет определиться с методом - какой контракт между ним и вызывающим его? 

Например, возьмем класс Iterator в джаве. По контракту, перед вызовом метода next() нужно вызывать метод hasNext(), иначе в некоторый момент можно поймать исключение NoSuchElementException. Ситуация вполне обыденная - мы часто пользуемся итераторами для прохождения по коллекциям, однако мы всегда соблюдаем контракт (и исключение заставляет нас это делать).

Поэтому при переводе денег, я думаю, лучше сделать вызов метода hasEnoughMoney - и с чистой совестью бросать эксепшн, если денег на счету нет. Тут -1 или null не вернешь - исключительная ситуация прямо напрашивается.

В случае с сервером все исключения, я думаю, можно объеденить по какому-то признаку, и бросать, скажем, два - AuthorisationException, ConnectException. Остальные можно не афишировать, но бросать так же (внутренняя ошибка сервера - большая проблема). В джаве для таких случаев для первых двух лучше использовать checked exception, для остальных - unchecked. Однако при правильной архитектуре сервера потребность в большом количестве исключений отпадет. Например, сделать метод isReachable() и возвращать булеву. После этого делать login() и бросать в нем ConnectionException (его сделать unchecked, если для джавы). Затем передавать какие-либо данные. Ну идея понятна, я думаю. 

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

Некоторые программисты делают так - создают два метода. Один бросает исключение, когда контракт не соблюден, другие же просто возвращают null. Например, poll() и remove() в интерфейсе Queue в джаве задают именно такой контракт. 

В целом, я думаю, нужно делать так, как удобнее. Проверять на -1 при чтении потока иногда удобнее, хотя я бы сделал метод isEof() и бросал бы исключение (как в паскале).

Насчет бесконечности при делении на ноль - так возвращается при некоторых условиях (F тут обозначает float):
Код

System.out.println(10F / 0);

Код

Infinity


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

Добавлено через 2 минуты и 45 секунд
Цитата(nikitao @  9.6.2011,  00:55 Найти цитируемый пост)
А если в ф-цию передаются неверные параметры ( там null к примеру в кач-ве одного из параметров) вы тоже код возвращаете ?

Нужно проверять это либо с помощью assert либо бросать AssertionException. Тут код, я думаю, возвращать неправильно - исключительная ситуация хорошо поможет в отладке, если каким-то образом случайно передается null. По стектрейсу не составит большого труда определить причину (в большинстве случаев).


--------------------
datatalks.ru - анализ данных, статистика, машинное обучение
PM MAIL WWW   Вверх
wester
Дата 9.6.2011, 15:21 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 675
Регистрация: 27.2.2009

Репутация: 4
Всего: 13



В последнем проекте использовал исключение только в одном случае исключение - если недоступен интернет-ресурс. В этом случае завершалась работа программы, ибо работать в таком случае нельзя. В остальных случаях я возвращал код ошибки через енумы.
По поводу передачи некорректного параметра - проверяю параметры на разных уровнях. К примеру у меня реализована работа с БД и заполнением ее данными из инета. Данные которые поступают от пользователя, проверяю на первом уровне и если все данные верны, то вызываю методы (второго уровня) заполнения БД. В противном случае возвращаю код ошибки. Этакий Code Contracts , только со своими плюсами.

маленькое обсуждение http://stackoverflow.com/questions/161942/...-net-exceptions

Это сообщение отредактировал(а) wester - 9.6.2011, 15:35
PM MAIL   Вверх
mihryak
Дата 9.6.2011, 17:34 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 731
Регистрация: 28.4.2007
Где: С-Пб

Репутация: 19
Всего: 36



Цитата(wester @  9.6.2011,  00:45 Найти цитируемый пост)
для биллинга исключения на мой взгляд лучше не закладывать, а все писать в большой лог.

т.е. попробовать списать с клиента деньги за услугу, узнать, что их не хвататет - ну и фиг с ним, потом в логе кто-нибудь заметит, так?

Цитата(Stolzen @  9.6.2011,  16:06 Найти цитируемый пост)
Поэтому при переводе денег, я думаю, лучше сделать вызов метода hasEnoughMoney - и с чистой совестью бросать эксепшн, если денег на счету нет. Тут -1 или null не вернешь - исключительная ситуация прямо напрашивается.

неприемлимо без блокирующей транзакции, т.к. между проверкой и списанием деньги может списать ещё кто-нибудь
так что в этом случае - исключение с откатом, и никак иначе!

я придерживаюсь следующих принципов для исключений:
- неправильные аргументы
- недопустимая операция для текущего состояния
- несколько несовместимых причин, из-за которых выполнить операцию невозможно (это исключает появление кодов ошибок c-style)
- код неспособен как-либо разумно поступить в сложившейся ситуации

например,
метод List<T>.Remove(T) может вполне себе спокойно вернуть false, если объект не найден
но Dictionary<TKey, TValue)[Tkey] не может придумать, что бы ему такого вернуть, если ключ не найден
ServiceClient.SendPost() может завалиться по совсем разным причинам, тут исключение, т.к. использовать коды ошибок в .нете не принято

PM MAIL ICQ   Вверх
Fieral
Дата 9.6.2011, 22:08 (ссылка) |    (голосов:2) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 250
Регистрация: 10.12.2007

Репутация: нет
Всего: 1



Для установки эксепшонов работает простое правило: Выбрасывай исключение только когда больше ничего сделать нельзя.

Это автоматически отвечает на вопрос: "Что такое исключительная ситуация?" - это когда больше ничего сделать нельзя.
Если можно - разруливай.

--------------------
Если собака свернулась калачиком, значит будет дождь, а если сидит выпучив глаза, значит у неё запор.
PM MAIL   Вверх
  
Ответ в темуСоздание новой темы Создание опроса
Прежде чем создать тему, посмотрите сюда:
mr.DUDA
THandle

Используйте теги [code=csharp][/code] для подсветки кода. Используйтe чекбокс "транслит" если у Вас нет русских шрифтов.
Что делать если Вам помогли, но отблагодарить помощника плюсом в репутацию Вы не можете(не хватает сообщений)? Пишите сюда, или отправляйте репорт. Поставим :)
Так же не забывайте отмечать свой вопрос решенным, если он таковым является :)


Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, mr.DUDA, THandle.

 
1 Пользователей читают эту тему (1 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | Общие вопросы по .NET и C# | Следующая тема »


 




[ Время генерации скрипта: 0.0727 ]   [ Использовано запросов: 21 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.