Почему я не люблю логику на исключениях
Feb. 15th, 2012 11:41 amОдин жабист утверждает что исключения надо ловить и обрабатывать, а не дать им нахрен свалится в корень вызова и пусть тот, кто вызывает, разбирается. Ну в жабе по другому сложнее, это понятно.
Но я не люблю обрабатывать исключения самостоятельно, за исключением "вывел в лог с данными приведшими к исключению и кинул дальше".
Потому что в таком случае баги становятся расходящимися. Хороший баг - это когда ВСЕ СДОХЛО, КОРОВЫ, ЗАБОРНИКИ, СТЕКТРЕЙС, МЯСО КРОВЬ КИШКИ. Умирает от входа. Сразу видно, где что надо исправлять.
А плохой баг: это когда он был, но мы его не видели, иначе как чтением логов каждый день за чаем. А хуже того - если мы исключение от него перехватили и пошли работать дальше, а в состоянии программы уже поселилось маленькое скрытое червие которое потом дальше где-нибудь приведет к еще худшим, уже непонятным ошибкам. Впрочем, такое редко бывает, если корректно работать - транзакции там при ошибках откатывать, внешние ресурсы закрывать в finally и вообще возвращаться в корректное состояние.
Но лучше все-таки вернутся в корректное состояние в finally, отписаться в лог в catch и бросить исключение дальше - пусть главный цикл оконных сообщений или там веб-сервер разбирается, у него голова больше.
Но я не люблю обрабатывать исключения самостоятельно, за исключением "вывел в лог с данными приведшими к исключению и кинул дальше".
Потому что в таком случае баги становятся расходящимися. Хороший баг - это когда ВСЕ СДОХЛО, КОРОВЫ, ЗАБОРНИКИ, СТЕКТРЕЙС, МЯСО КРОВЬ КИШКИ. Умирает от входа. Сразу видно, где что надо исправлять.
А плохой баг: это когда он был, но мы его не видели, иначе как чтением логов каждый день за чаем. А хуже того - если мы исключение от него перехватили и пошли работать дальше, а в состоянии программы уже поселилось маленькое скрытое червие которое потом дальше где-нибудь приведет к еще худшим, уже непонятным ошибкам. Впрочем, такое редко бывает, если корректно работать - транзакции там при ошибках откатывать, внешние ресурсы закрывать в finally и вообще возвращаться в корректное состояние.
Но лучше все-таки вернутся в корректное состояние в finally, отписаться в лог в catch и бросить исключение дальше - пусть главный цикл оконных сообщений или там веб-сервер разбирается, у него голова больше.
no subject
Date: 2012-02-15 08:46 am (UTC)no subject
Date: 2012-02-15 08:53 am (UTC)Ну и из реальной практики, лучше иметь внешне работающую неработающую программу, чем внешне неработающую работающую. Кровь и мясо - это для разработчика хорошо, а клиент от них нервничает.
no subject
Date: 2012-02-15 09:01 am (UTC)Круто потом фиксить продакшен базу, после того как её пол года грыз червь.
no subject
Date: 2012-02-15 09:55 am (UTC)А вообще конечно лучше быть и здоровым и богатым и умным и сразу всё делать чётко без ошибок.
no subject
Date: 2012-02-15 10:10 am (UTC)no subject
Date: 2012-02-15 10:42 am (UTC)no subject
Date: 2012-02-15 08:59 am (UTC)no subject
Date: 2012-02-15 11:19 am (UTC)no subject
Date: 2012-02-15 09:26 am (UTC)http://mobwiki.ru/%D0%A4%D0%B8%D0%BB%D0%BE%D1%81%D0%BE%D1%84%D0%B8%D1%8F_UNIX#.D0.A0.D0.B5.D0.B9.D0.BC.D0.BE.D0.BD.D0.B4:_.D0.98.D1.81.D0.BA.D1.83.D1.81.D1.81.D1.82.D0.B2.D0.BE_.D0.BF.D1.80.D0.BE.D0.B3.D1.80.D0.B0.D0.BC.D0.BC.D0.B8.D1.80.D0.BE.D0.B2.D0.B0.D0.BD.D0.B8.D1.8F_.D0.B2_UNIX
правило восстановления
no subject
Date: 2012-02-15 04:18 pm (UTC)no subject
Date: 2012-02-15 04:37 pm (UTC)no subject
Date: 2012-02-15 04:38 pm (UTC)no subject
Date: 2012-02-15 10:03 am (UTC)Во-вторых, проблему определения точки обработки исключения никто окончательно не решил (на любом языке), это эвристическая инженерная задача, иногда очень сложная из-за дополнительных требований типа "спросить у юзера, если не против, то проигнорировать 1 ошибку в пакете и продолжить дальше." Мир вообще местами сложен. Если вам какой-то пионер советует всегда сразу ловить и сразу обрабатывать все исключения -- ну пометьте себе, что он иногда гонит туфту.
В-третьих, а какая альтернатива исключениям? errno?
no subject
Date: 2012-02-15 10:38 am (UTC)no subject
Date: 2012-02-15 11:46 am (UTC)no subject
Date: 2012-02-15 10:41 am (UTC)Я обычно делаю так: для заведомых ошибок - исключения. Пусть валится.
Для исключений "не у меня" - перехват и далее - или выброс дальше, или же обработка и код возврата.
Для операций, которые по определению могут быть ошибочными (пользователь ввел не тот пароль) - код возврата.
Коды возврата офомлены методом "имитируем хаскель на java/c#" - базовый класс-генерик с двумя наследниками - один сигнализирует об ошибке и тащит с собой информацию о ней, второй об удаче и тащит с собой результат. В параметрах класса-генерика - тип для удачного результата, и тип для неудачного результата.
no subject
Date: 2012-02-15 11:29 am (UTC)no subject
Date: 2012-02-15 11:46 am (UTC)Иногда исключение, иногда сообщение для пользователя читабельное, все равно на уровне GUI никто не знает что с этой ошибкой делать, кроме "показать сообщение пользователю и повторить попытку".
no subject
Date: 2012-02-15 11:47 am (UTC)no subject
Date: 2012-02-15 11:58 am (UTC)no subject
Date: 2012-02-15 10:05 am (UTC)no subject
Date: 2012-02-15 10:57 am (UTC)no subject
Date: 2012-02-15 11:05 am (UTC)Например, есть библиотека, читающая utf-8 поток, она используется в библиотеке, которая парсит xml, а эта библиотека используется в коде юзера, который, например, читает какой-нибудь конфиг.
И тут раз, в потоке встретился невалидный utf-codepoint, и самый нижний уровень выкидывает исключение (кондишн). В java-like языках на этом месте уже ничего не сделать (конфиг не прочитан), а в CL можно ругнуться в лог и вызвать рестарт первой библиотеки: "Замени этот мусор на пробел". После этого вся конструкция спокойно дочитает конфиг.
Конечно, подобное поведение можно спрограммировать и в джаве: например, передавать вниз по стеку какой-нибудь флаг "игнорировать невалидные символы". Профит CL здесь в том, что рестарт программируется только один раз в том месте, где может возникнуть исключительная ситуация (в самой первой библиотеке вычитывания юникодного потока), а все остальные уровни про него знать не обязаны. В джаве же в моём примере разборщик xml-я должен иметь в виду, что вычитывальщик юникода может обломаться в данной конкретной ситуации. Ну и реакция на исключение может быть гораздо гибче настроена.
В качестве дополнительного бонуса: непойманный никем рестарт вылавливает REPL и, в режиме диалога, предлагает варианты реакции уже программисту :) Например, по исключению типа 'file-not-found можно тут же в репле поправить путь до файла и продолжить выполнение программы, а не рестартить весь проект.
no subject
Date: 2012-02-15 11:56 am (UTC)2. исключения в интерфейсах -- ад, мерзкий и кошмарный. Обычно он проявляется либо в необходимости везде ловить исключения, либо в исключениях, идущих из глубины, через несколько уровней библиотек/модулей.
Если возможно, библиотека/модуль должны предоставлять такой интерфейс, в котором вызов функции библиотеки/модуля возвращает только значение, конкретное и чёткое, а не заставляет оборачивать вызов в ловлю исключения.
(кстати, к лентяйке у меня подобная претензия -- там применение аргумента к функции возвращает не значение, а задержанное вычисление, которое 1. может свалиться где-нибудь в другой точке программы или зациклиться, 2. будет использовать ресурсы (процессор, память) в другой точке программы. при строгой модели вычислений таковых проблем нет: если значение есть, то оно есть, оно не может быть "ошибкой вычисления", программа упадёт именно при создании этого значения -- при вызове библиотечной функции, например. и, если значение есть, то с ним можно работать любыми способами, которыми возможно, без необходимости что-то вычислять.)
no subject
Date: 2012-02-15 12:50 pm (UTC)no subject
Date: 2012-02-15 12:52 pm (UTC)no subject
Date: 2012-02-15 07:48 pm (UTC)no subject
Date: 2012-02-16 07:08 am (UTC)