IBM: Теория и практика Java: Правильные навыки ведения домашнего хозяйства Статья () с интересным заголовком повествует о сборке мусора в Java, а точнее о пределах возможностей сборщика мусора и тех ситуациях, когда самостоятельная уборка в доме просто необходима. |
Комментарии |
Комментарии: 952
| написал(а) ...
public void enumerateBar() throws SQLException { Statement statement = null; ResultSet resultSet = null; Connection connection = getConnection(); try { statement = connection.createStatement(); resultSet = statement.executeQuery("SELECT * FROM Bar"); // Use resultSet } finally { try { if (resultSet != null) resultSet.close(); } finally { try { if (statement != null) statement.close(); } finally { connection.close(); } } } }
|
|
Комментарии: 178
Зарегистрирован: 24.03.2005 17:32
| Да этот батенька извращенец, писать такой код! Можно подумать, что если resultSet закрыть не удалось, то это должно пораждать исключение. Да и глубоко вложенные конструкции всегда являлись плохим тоном программирования. Короче, если продвинутый дядька пишет такой дурацкий код, то это повышает мое самомнение |
|
Комментарии: 178
Зарегистрирован: 24.03.2005 17:32
| Я бы ему посоветовал такую штуку: public void enumerateBar() throws SQLException { Statement statement = null; ResultSet resultSet = null; Connection connection = null; try { connection = getConnection(); statement = connection.createStatement(); resultSet = statement.executeQuery("SELECT * FROM Bar"); // Use resultSet } finally { if (resultSet != null) try { resultSet.close(); } catch (SQLException ex) {} if (statement != null) try { statement.close(); } catch (SQLException ex) {} if (statement != null) try { connection.close(); } catch (SQLException ex) {} } } |
|
Комментарии: 178
Зарегистрирован: 24.03.2005 17:32
| ой облажался в четвертой строчке от конца. Там должно быть if (connection != null) |
|
Комментарии: 78
Зарегистрирован: 18.08.2005 04:25
| Когда я вижу такой код, то готов согласиться с последователями Вирта, что исключения суть зло. Абракадабра... |
|
Комментарии: 178
Зарегистрирован: 24.03.2005 17:32
| Исключения не зло, помогают писать хорошо работающий код, Но, как и везде, нужно знать меру. |
|
Комментарии: 78
Зарегистрирован: 18.08.2005 04:25
| Исключения не зло, а конструкция языков программирования. На мой взляд, исключения помогают писать хорошо умирающий код. Поясню. Исключения следует использовать только в исключительных случаях. Как говорится, мертвые программ не лгут. Когда нормальное продолжение работы уже не возможно, программа может честно умереть по исключению, выплюнув на stderr информацию, которая поможет при отладке. Использование исключений так, как в примере выше, это пример плохого дизайна. Логика программы должна быть реализована посредством обычных условий и циклов, а не кучей вложенных try. Я не знаю как в java, но в Си++ ветка finally вообще не нужна (и отсутствует в синтаксисе), а ветка except тоже часто бывает излишней: если нужно гарантировать, что некий код всегда будет отрабатывать, независимо от того, произошло ли исключение, то этот код можно поместить в деструктор класса, а объект этого класса разместить в виде стековой переменной. Язык гарантирует вызов деструкторов таких объектов. |
|
Комментарии: 178
Зарегистрирован: 24.03.2005 17:32
| В Java к счастью или к сожалению (кому как) исключения используются гораздо чаще и не носят такой уж исключительный характер. Исключение это всеголишь индикация того, что чтото пошло не так. Достаточно часто это "не так" можно с легкостью обработать и обезвредить. И, по опыту, могу утверждать что использование исключений привносит в поведение программы больше предсказуемости. Но, опять же, все должно быть в меру. К тому же конструкция try-catch гораздо элегантнее, чем анализ возвращенного значения из метода.
|
|
Комментарии: 78
Зарегистрирован: 18.08.2005 04:25
| К тому же конструкция try-catch гораздо элегантнее, чем анализ возвращенного значения из метода. Исходя из своего опыта, я пришел к такому выводу: если возникает желание понавтыкать в методы веток try-catch, значит пора садиться и перепроектировать иерархию классов. Хотя конечно, если некая библиотека спроектирована с помощью интенсивного использования исключений, то тут никуда не денешься - придется пользоваться. Иногда исключения можно использовать нетривиальным способом. Писал я как-то интерпертатор одного простенького сценарного языка, и возникла потребность обеспечить функционирование оператора break, прерывающего циклы. break мог быть глубоко вложен в стек интепретации, и прерывать сразу несколько вложенных циклов. Решение с передачей возвращаемого значения требовало модификации всех методов, обеспочивающих логику операторов языка. Поэтому я при иницировал исключение в break-методе, а в объемлющем while-методе это исключение ловил. По сути, это хак - так программировать нельзя. |
|
Комментарии: 558
| Соглашусь с предыдущим оратоором. Ж) Правда я на жаве не пишу... я С++ник...
Но у меня тоже не возникает необходимости юзать исключения.
Нынешний мой проект на работе содержит токльо одну связку try/catch при вызове либы... а в остальном очень красиво... горжусь... целиком писал сам... (первый целиком собственный проект за 6 лет работы Ж)
а вот предыдущий проект - был ужасен. макросами вида if (FAILED(....)) throw ... была обрамлена каждая функция WinAPI... потом все тело функции было замернуть в try/catch а функция в итоге возвращала DWORD...
это получатеся как Цифро-Аналого-Цифровой преобразователь... Ж) то есть абсолютнео бесполезно...
Вообще на любом языке можно извратиться так, что ни один парсер или сборщик мусора не разберется...
но программировать так нельзя... к сожалению это понимаешь только после 10 лет программирования... Ж) |
|
Комментарии: 78
Зарегистрирован: 18.08.2005 04:25
| Хоть это и не имеет прямого отношения к теме исключений, процитирую отрывок из книги "Программис-прагматик":
В самом начале своей профессиональной карьеры каждый программист обязан выучить некую мантру. Она представляет собой фундаментальную основу компьютерных вычислений, основное вероучение, которое мы учимся применять к требованиям, конструкциям, самим программам, комментариям - словом, всему, что мы делаем. Оно звучит так:
"Этого никогда не случится..."
И далее: "Через 30 лет эта программа использоваться не будет, так что для обозначения года хватит и двух разрядов"". "Нужна ли интернационализация, если это приложение не будет использоваться за рубежом?" "Счетчик не может принимать отрицательное значение". "Этот оператор printf не дает сбоев". Не стоит заниматься подобного рода самообманом, особенно при написании программ.
Если что-то не может произойти, воспользуйтесь утверждениями, которые гарантируют, что это не произойдет вовсе. Я применяю для этого макросы ASSERT и MUST_SUCCEED, которые выдают диагностическое сообщение и прибивают программу, если что-то пошло не так. Может быть кому-то покажется экстремизмом проверять значения, возвращаемые даже такими функциями, как fputc или fclose, но зато это дает намного больше уверенности, что программа либо будет работать правильно, либо умрет, не испортив никаких данных. Если речь идет о Си++, эти макросы могут иницировать исключение, а если о Си - просто вызывать exit(1). |
|
Комментарии: 558
| «Паранойя — профессиональное заболевание специалистов по безопасности. Но любители могут в этой области зайти гораздо дальше» (c) В. Гайкович.
Кстати автор цитаты - мой директор.
нет смысла обкладывать макросами fclose, если глюк в нем не нарушит работоспособности программы. пусть он хоть навсегда останется открытым, этот файл.
Но конечно результат fopen обязательно надо проверять! |
|
Комментарии: 558
| Кстати вот в винде интересный глюк...
по долгу службы занимаюсь публикацией сертификатов/CRL в LDAP-хранилище...
тут короче попался CRL 87 килобайт... лдап то его без вопросов проглотил, а вот ADCEdit (модуль mmc для ковыряния с AD) при попытке поглядеть это поле на Win2000 все mmc встает раком... только убивать... хотя вот на 2003 исправились... говорит что поле больше 1килобайта не может быть отображено.
Вы спросите: К чему это я? просто в тему... проверочку пожалели... |
|
Комментарии: 178
Зарегистрирован: 24.03.2005 17:32
| На самом деле идет речь не о С++, а о Жабе. В Жабе при объявлении метода можно использовать ключевое слово throws: <div class='codec'>void myMethod() throws SomeException { ... }</div>Это значит, что данное исключение вызывающий метод должен обработать в catch либо так же объявить. И эта фича юзается во всех классах платформы. А С++ это сравнивать не стоит, ибо возвращаемые значения далеко не всегда можно проанализировать, например, метод может возвращать к.н. объект, типа: <div class='codec'>MyObject readMyObjectFromBuggyNet() throws IOException </div>так как в жабе нет возможности обратиться к указателям - метод, с точки зрения программиста, возвращает объект а не адрес памяти. То это значение максимум мы можем проверить токо на null, чего не достаточно, если причин сбоев может быть несколько. Исключения тут помогают очень хорошо. К тому же программа легче читается - обработка ошибок и анализ возвращенного значения разделены.
|
|
Комментарии: 78
Зарегистрирован: 18.08.2005 04:25
| «Паранойя — профессиональное заболевание специалистов по безопасности. Но любители могут в этой области зайти гораздо дальше» (c) В. Гайкович. Золотые слова! Кстати, глава книги, откуда я взял цитату, называется "Прагматическая паранойя". Как сказали сами авторы, программист-прагматик не доверяет никому: ни другим, ни себе, ни, тем более, - авторам этой книги.
нет смысла обкладывать макросами fclose, если глюк в нем не нарушит работоспособности программы. пусть он хоть навсегда останется открытым, этот файл. Можно привести массу примеров, когда это реально помогает. Например переполнение буфера затерло значение переменной FILE* file. (А если этот буфер был объявлен как FILE* files[], то результаты будут исключительно интересными...) Так что здесь всё зависит как раз от степени паранойдности.
Но конечно результат fopen обязательно надо проверять! К сожалению, я видел слишком много программ, в которых этого не делалось. Значение таких вещей, как fputc тоже надо обязательно проверять - вдуг на диске кончилось место, или произошло еще что-то такое. Но этого тоже почти никто не делает.
Это значит, что данное исключение вызывающий метод должен обработать в catch либо так же объявить. Это называется Хорошая Идея. И в таком контексте исключения исключительно полезны (нечаянный каламбур ).
К тому же программа легче читается - обработка ошибок и анализ возвращенного значения разделены. А вот это как раз пример плохой идеи. Если обработка ошибок и/или анализ возвращенного значения занимают столько места, что их приходится искуственно разносить подальше с помошью catch-ветки, то это очень плохой дизайн кода. Хорошая ОО-программа состоит из компактных и мобильных фрагментов кода, назначение явно видно прямо по тексту. ИМХО. |
|
Комментарии: 178
Зарегистрирован: 24.03.2005 17:32
| Если обработка ошибок и/или анализ возвращенного значения занимают столько места, что их приходится искуственно разносить подальше с помошью catch-ветки, то это очень плохой дизайн кода.
Разносить это как раз то логичнее, красивее и понятнее. Так как обработка данных и обработка ошибок разные вещи. При этом не имеет значение анализ занимает одну строчку или сто, хотя, должен согласиться, что на анализ не должен занимать много строк, ибо "...компактных и мобильных фрагментов..." |
|
Комментарии: 83
Зарегистрирован: 04.07.2004 21:44
| Vadim Ushakov написал(а) ... Когда я вижу такой код, то готов согласиться с последователями Вирта, что исключения суть зло. Зло.
Vadim Ushakov написал(а) ... Абракадабра... Абракадабра.
А что скажет Джоэль? http:/www.joelonsoftware.com/items/2003/10/13.html
Понятие "исключение" противоположно понятию "транзакция". Если исключение вылетит в "неучтённом" месте, данные могут остаться в противоречивом состоянии. Ещё хуже если не просто данные в памяти а внешние ресурсы. И механизм исключений не даёт никаких средств откатить незавершённую транзакцию.
Vadim Ushakov написал(а) ... Можно привести массу примеров, когда это реально помогает. Например переполнение буфера затерло значение переменной FILE* file. Угу.
Даже если удалось поймать исключение, то масштаб разрушений - неизвестен. |
|
Комментарии: 178
Зарегистрирован: 24.03.2005 17:32
| И механизм исключений не даёт никаких средств откатить незавершённую транзакцию. Да ну!? Докажи! <div class='codec'>try { transaction.begin(); //bla bla bla //bla bla bla transaction.commit(); } catch (Exception ex) { transaction.rollback(); } </div>Даже если удалось поймать исключение, то масштаб разрушений - неизвестен. Я плачу. Что значит "Даже если удалось"? Будто ты с сачком бегаешь и пытаешься отловить исключение, которое в самый последний момент от тебя "упархивает".
Хотя, я С++ глубоко не знаю, но в Java, о котором идет речь, с исключениями все надежно. Если ты отлавливаешь исключение - оно отлавливается 100%, если не отлавливаешь - то и ожидать нечего.... |
|
Комментарии: 78
Зарегистрирован: 18.08.2005 04:25
| try { transaction.begin(); //bla bla bla //bla bla bla transaction.commit(); } catch (Exception ex) { transaction.rollback(); } Это на Жабе? Если это был бы Си++, то этот же код выглядел бы проще, при условии что деструктор транзакции вызывает rollback:
transaction.begin(); //bla bla bla //bla bla bla transaction.commit();
В Си++ основная польза исключений не столько в try-catch, сколько в автоматическом вызове деструкторов при раскрутке стека. Насколько я понимаю, в Жаве для этого юзается finally. Си++ не производит контроля списка исключений, которые указываются в throw-блоке в описании функций. Все эти списки приходится проверять вручную, а значит какое-то исключение можно просто не заметить. Я не против исключений вообще - я против нелогичный реализаций этого механизма. Жавой я почти не пользуюсь, а в Си++ лучше избегать исключений, чтобы не наступать потом на собственноручно написанные грабли.
Понятие "исключение" противоположно понятию "транзакция". Если исключение вылетит в "неучтённом" месте, данные могут остаться в противоречивом состоянии. Ещё хуже если не просто данные в памяти а внешние ресурсы. И механизм исключений не даёт никаких средств откатить незавершённую транзакцию. Не согласен. Исключения - это не механизм транзакций, и нечего его обвинять в том, что он чем-то НЕ ЯВЛЯЕТСЯ. Исключения - это механизм, предназначенный для проведения локальной сборки мусора. В Си++ иницирование исключения запускает раскрутку стека и приводит к вызову по очереди всех деструкторов объектов, удаляемых из стека, пока не найдется точка, в которой исключение может быть обработано. Именно в раскрутке стека заключается вся "соль", а не в try-catch. Вызов transaction.rollback() в примере выше - это частный случай такой сборки мусора. Мы освобождаем захваченные ресурсы. В данном случае мы отменяем транзакцию и освобождаем блокировку в базе данных. Независимо от типа исключения, независимо от того, поймаем мы его ли нет, все внешние ресурсы будут освобождены. То, что они могут быть в противоречивом состоянии - это не проблема механизма исключений, это классическая проблема обеспечения целостности. Если алгоритм может нарушить целостность структур данных - журналируй все действия. А исключения тут не при чем. Если произойдет сбой питания, не исключения же винить, что данные разрушились. В Java, на мой взгляд, потребность в исключениях меньше, поскольку там есть автоматический сборщик мусора (и стековых объектов там нет, если я не ошибаюсь). Но у меня нет большого опыта программирования на этом языке. fedukoff утверждает, что в Java исключения очень полезны - поверю ему на слово. |
|
Комментарии: 178
Зарегистрирован: 24.03.2005 17:32
| В Java, на мой взгляд, потребность в исключениях меньше, поскольку там есть автоматический сборщик мусора (и стековых объектов там нет, если я не ошибаюсь).
В Java не используя механизма исключений, практически невозможно написать ни одной более-менее работающей программы, ибо в API платформы, большенство методов объявляются с ключевым словом throws, что заставляет программиста как либо обрабатывать возможные исключения. Иначе compile type error. А сборщик мусора это сборщик мусора, при том что исключения это исключения. Они не взаимоссязаны. Задача сборщика мусора - освобождать память от неиспользуемых объектов. Задача исключений - "информировать" workflow об исключительных ситуациях. При этом в Java отсутсвуют деструкторы как таковые, как раз из-за того, что освобождением объектов занимается GC. Есть, конечно, метод finalize() который вызывается сборщиком мусора перед тем как удалить объект из памяти, но уповать на то что этот метод вызовется и освобождать в нем к.н. ресурсы - делать большую ошибку, так как этот метод вообще может не вызваться, если GC решит, что освобождать память от этого объекта низя, или программа завершится раньше, чем GC что либо освободит. |
|
Комментарии: 178
Зарегистрирован: 24.03.2005 17:32
| сорри. Compile type error = Compile time error |
|
Комментарии: 78
Зарегистрирован: 18.08.2005 04:25
| Ну так я о этом и говорю. А сборку мусора можно понимать в широком смысле как освобождение ресурсов, отмену транзакций, закрытие сетевых соединений и т.п. try-catch/finally разве не для этого служит? finally - это такой своебразный эквивалент деструкторов. А new/delete памяти - это частный случай сборки мусора, при чем не самый сложный. |
|
Комментарии: 178
Зарегистрирован: 24.03.2005 17:32
| А сборку мусора можно понимать в широком смысле как освобождение ресурсов, отмену транзакций, закрытие сетевых соединений и т.п.
Сборку мусора ни в коем случае нельзя понимать как освобождение таких ресурсов, как транзакции и сетевые соединения. Это всеголишь освобождение памяти. Конечно, же перед тем как удалить объект из памяти, как я говорил, сборщик вызывает метод finalize() у объекта, в котором, например, у объекта Connection, реализовано закрытие соединения. Но не закрывать соединение в ручную, после использования, расчитывая, что это сделает сборщик мусора, как нидь сам и потом - крайне не хороший и не красивый стиль.
try-catch/finally разве не для этого служит?
try-catch-finally можно использовать и для этого, но эта конструкция никак не связана со сборщиком мусора. Изначально отлов исключений служит для востановления рабочего состояния после сбоя, журналирования или, другими словами, принятия правильного решения, что же с этим делать дальше.
catch-finally - наиболее подходящие места для освобождения ресурсов (закрытия соединений, откат транзакций), но это больше частный случай, чем предназначение отлова исключений. И опять же повторюсь: сборщик мусора тут совсем ни при чем. Он всеголишь освобождает память от неиспользуемых, недостижимых объектов, т.е. тех, на которые нету ссылок. Т.е. получается, что даже после того, как возбудилось исключение, скажем, в обработке запроса к БД, соединение (Connection) или другие объекты, учавствующие в этом запросе, не будут удалены из памяти, если ссылки на них объявлены в глобальном контексте (т.е. эти объекты все еще достижимы). |
|
Комментарии: 78
Зарегистрирован: 18.08.2005 04:25
| fedukoff, хватит пересказывать мне мои же фразы!
catch-finally - наиболее подходящие места для освобождения ресурсов (закрытия соединений, откат транзакций) Применительно к Java, это ЕДИНСТВЕННЫЕ подходящие места для гарантированного освобождения ресурсов.
И опять же повторюсь: сборщик мусора тут совсем ни при чем. Он всеголишь освобождает память от неиспользуемых, недостижимых объектов, т.е. тех, на которые нету ссылок. Блин, что ты к сборщику мусора привязался? Я привел пример из Си++, где память нужно освобождать наравне с любыми другими ресурсами. Кстати, я не с тобой спорю, а с Капитаном: у нас с ним давние разногласия по этому поводу. Пример кода, с которого началось это обсуждение, как раз и дает возможность сторонникам Вирта говорить о вредности исключений. А я говорю - это неправильный пример. А если язык или библотека вынуждает программиста использовать такие зубодробительные конструкции - это неправильный язык и неправильная библиотека. Короче, все дело в пчелах и неправильном мёде. |
|
Комментарии: 178
Зарегистрирован: 24.03.2005 17:32
| Ок. Замяли... Просто люблю точность |
|
Комментарии: 83
Зарегистрирован: 04.07.2004 21:44
| fedukoff написал(а) ... Да ну!? Докажи! try { transaction.begin(); //bla bla bla //bla bla bla transaction.commit(); } catch (Exception ex) { transaction.rollback(); } Что мы видим? Мы видим семантическую связность между transaction.* и //bla bla bla
Это означает что они взаимодействуют через глобальный контекст.
И поэтому каждый раз когда мы изменяем //bla bla bla мы должны также проверить transaction.* чтобы убедиться что он ещё работает. И если забыть/ошибиться, код станет некорректным.
fedukoff написал(а) ... Что значит "Даже если удалось"? Исключение - это исключительная ситуация. Или иначе "непредусмотренная ситуация".
Введение в язык механизмов обработки исключений - это попытка "предусмотреть непредусмотренные ситуации".
Однако это никоим образом не гарантирует что помимо "предусмотренных непредусмотренных ситуаций" не останется "непредусмотренных непредусмотренных ситуаций".
"Даже если удалось" означает "даже если удалось предусмотреть". Если при этом что-то предусмотреть не удалось, то это может сделать обработчик исключения некорректным, и вместо решения проблемы мы гребём дополнительные.
Vadim Ushakov написал(а) ... В Си++ иницирование исключения запускает раскрутку стека и приводит к вызову по очереди всех деструкторов объектов, удаляемых из стека, пока не найдется точка, в которой исключение может быть обработано. Именно в раскрутке стека заключается вся "соль", а не в try-catch. Замечательно.
А теперь вспомним что при выходе автоматического объекта из области видимости неявно вызывается деструктор. То есть выход из области видимости - это тоже "раскрутка стека", но без try-catch. То что нужно. Тогда обработка исключений в языке - не нужна. Правильно? |
|
Комментарии: 178
Зарегистрирован: 24.03.2005 17:32
| Что мы видим? Мы видим семантическую связность между transaction.* и //bla bla bla Это означает что они взаимодействуют через глобальный контекст.
Связность может мы и видим. Но это нифига не означает, что они взаимодействуют через глобальный контекст. Объект transaction вполне мог быть создан (выбран из пула, как вариант) непосредственно перед использованием. А это гарантирует то что он существует. Если я такой код напишу: <div class='codec'>try { Transaction transaction = new Transaction(); transaction.begin(); //bla bla bla //bla bla bla transaction.commit(); } catch (TransactionCreationException ex { // Ooops } catch (Exception ex) { transaction.rollback(); }</div>Получается, что если не возможно создать transaction, то возбуждается исключение TransactionCreationException, и следовательно код, работающий с транзакцией не отработает, но мы прекрасно сможем обработать такую ситуацию (строчка Ooops). Потом, если мы не можем дойти до commit, или по каким то причинам произвести этот commit, то обязательно произойдет rollback.
Повторяюсь. Мы говорим о Java. Толковая жабовая библиотека определяет для каждого метода, какие исключения этот метод может генерировать. Следовательно, не предусмотреть их не получится (ошибка во время компиляции).
Конечно, же существует два подкласса исключений RuntimeException и Error, которые можно не обрабатывать. К исключениям времени выполнения относятся исключения, типа OutOfMemoryException, NullPointerException, т.е. такие исключения, которые могут возникнуть где угодно и как угодно, заставлять их обрабатывать было бы кощунством и пыткой для программиста, но тем не менее, думать про них стоит. К исключениям-ошибкам относятся очень серьезные исключения типа ThreadDead, VirtualMachineError, которые, предсказать невозможно, но во всяком случае попытаться можно хоть в лог записать...
По поводу С++ ничего утвержать не буду, ибо не специалист... |
|
Комментарии: 178
Зарегистрирован: 24.03.2005 17:32
| ой. Я как всегда ошибок наделал код должен выглядеть иначе Transaction transaction = null; try { transaction = new Transaction(); transaction.begin(); //bla bla bla //bla bla bla transaction.commit(); } catch (TransactionCreationException ex { // Ooops } catch (Exception ex) { transaction.rollback(); } |
|
Комментарии: 78
Зарегистрирован: 18.08.2005 04:25
| Исключение - это исключительная ситуация. Или иначе "непредусмотренная ситуация".
Введение в язык механизмов обработки исключений - это попытка "предусмотреть непредусмотренные ситуации". Совершенно неверная трактовка исключений. Исключения - это механизм создания априорных правил поведения исполнителя в ситуациях, когда нормальное продолжение работы невозможно по тем или иным причинам. В некотором роде, это "ситуации, которых не должно быть".
Чтобы почувствовать потребность в априорных правилах поведения, представим себе заводского технолога, планирующего последовательность операций по изготовлению некоторой детали (аналогия с программированием довольно очевидна). Описание технологии (технологическая карта) предусматривает, скажем, отливку, затем фрезеровку поверхностей и расточку отверстий. Каждый из этапов расписывается в технологической карте довольно подробно. Однако все подробности касаются особенностей создания именно нужной детали. Общие (априорные) правила поведения исполнителей подразумеваются соответствующими их квалификации. В технологической карте, конечно, не сказано, что должен делать фрезеровщик, если выйдет из строя фреза, если возникнет пожар, землетрясение, нападение противника. Если бы технолог был вынужден планировать поведение исполнителя в любых ситуациях, то он никогда не закончил бы работу над такими "технологическими картами". Вот именно на случай "пожаров, землетрясений и нападений противника" и существует механизм исключений. А вовсе не для того, чтобы "предусмотреть непредусмотренные ситуации". На случай пожара предусмотрен план эвакуации. Но это не значит, что "алгоритме фрезеровщика" должен после каждого шага идти пункт "Проверьте не начался ли пожар, если начался продолжить выполнение алгоритма с такого-то пункта".
Является ли, например, неудача при открытии файла исключительной ситуацией? Скорее всего нет. Такая ситуация вполне обычна и должна быть обработана с помощью явных правил: можно вывести сообщение на stderr, можно отобразить диалоговое окно с предложением выбрать другой файл, можно попытаться открыть некий файл по умолчанию - всё это зависит от конкретного контекста и решаемой задачи. С другой стороны, если нам точно известно, что файл должен быть и его отсутствие недопустимо (например речь идет о динамической библиотеке), то это определенно исключительная ситуация. Такие "низкоуровневые" сбои, как отсутствие памяти, обращение по NULL-указателю, переполнение, деление на 0, выход за границы вектора и т.п. практически всегда являются исключительными ситуациями.
А теперь вспомним что при выходе автоматического объекта из области видимости неявно вызывается деструктор. То есть выход из области видимости - это тоже "раскрутка стека", но без try-catch. То что нужно. Тогда обработка исключений в языке - не нужна. Правильно? Это софистика. Вызов деструкторов и раскрутка исключения - две стороны одной медали в Си++. Там, где они не связаны (в Java), и механизм исключений работает совершенно иначе (finally-секция). В случае с нашим воображаемым "алгоритмом фрезеровщика" на случай пожара может быть такой пункт: "Отключить станок, покинуть помещение, вызвать пожарную команду". В программах действия в исключительных ситуациях гораздо более однобразны: обычно все сводится к освобождению занятых программой ресурсов и аварийному завершению процесса. На мой взгляд, в языках с автоматической сборкой мусора исключения играют менее важную роль, чем например, в Си++, поскольку в 90 процентах случаев освобождение ресурсов - это освобождение памяти.
Что мы видим? Мы видим семантическую связность между transaction.* и //bla bla bla
Это означает что они взаимодействуют через глобальный контекст.
И поэтому каждый раз когда мы изменяем //bla bla bla мы должны также проверить transaction.* чтобы убедиться что он ещё работает. И если забыть/ошибиться, код станет некорректным. Каким образом из этого "доказательства" следует доказываемое утверждение:
И механизм исключений не даёт никаких средств откатить незавершённую транзакцию. Либо весь "bla bla bla" исполняется корректно и мы доходим до transaction.commit(), либо мы попадаем в ветку catch и вызываем transaction.rollback(). Третьего не дано. |
|
Комментарии: 83
Зарегистрирован: 04.07.2004 21:44
| Следующий вариант:
в процессе сопровождения в "bla bla bla" были внесены дополнения, включающие захват дефицитного внешнего ресурса. Соответственно, следовало бы добавить в transaction.rollback() его освобождение. Если забыли это сделать, имеем утечку ресурса. |
|
Комментарии: 83
Зарегистрирован: 04.07.2004 21:44
| Про фрезеровщика особенно интересно.
Прежде всего исключение - это всегда реакция на действие. Мы захотели разделить на нуль а нам в ответ исключение: "слюшай, дарагой, зачэм тэбе на нол делить, да?".
Пожар - это реакция на какое действие фрезеровщика? Скорее всего пожар - это результат чьих-то других действий. То есть речь идёт о параллелизме. Но исключения не являются механизмом взаимодействия параллельных процессов.
Имеются и отличия между реальным фрезеровщиком и его имитационной программной моделью. Зачем фрезеровщику покидать здание при пожаре? Потому что иначе "он испортится" (с). Сделать новых людей - не так просто. Сделать новых квалифицированных фрезеровщиков - ещё сложнее. С другой стороны программный код и данные - это информация. Информация может очень дёшево многократно воспроизводиться, пока не заполнит всю память. "Отключить станок, покинуть помещение, вызвать пожарную команду" - это хорошо подходит для реального фрезеровщика, так как помогает сохранить этого самого фрезеровщика. Для компьютерно моделируемого фрезеровщика это бессмысленая трата процессорного времени и труда программистов. Ему гораздо лучше подошли бы действия "стоять на месте и ничего не делать", которые не требуют ни процессорных ресурсов, ни программного кода. И встроенных в язык исключений тоже. |
|
Комментарии: 178
Зарегистрирован: 24.03.2005 17:32
| в процессе сопровождения в "bla bla bla" были внесены дополнения, включающие захват дефицитного внешнего ресурса. Соответственно, следовало бы добавить в transaction.rollback() его освобождение. Если забыли это сделать, имеем утечку ресурса.
если захват к.н. ресурса происходит в процессе "bla bla bla", то освобождение этого ресурса, нужно включить в catch- или finally-блок. В данном контексте transaction может быть какимнидь буфером, который наполняется в процессе "bla bla bla", при commit() этот буфер переходит в другое состояние, а при rollback() этот буфер просто очищается. "Разделяй и властвуй" еще ни кто не отменял. К тому же освобождение захваченых ресурсов должно быть в области видимости (визуально) его захвата, а не в другом файле, другом классе или другой подсистеме.... Иначе, простите, это плохой стиль. |
|
Комментарии: 240
Зарегистрирован: 01.07.2004 14:57
| К примеру Капитана: кто что куда может забыть добавить?
Если какие-то действия выполняются в рамках транзакции, при вызове Rollback() они должны откатиться автоматически, иначе транзакций нет.
Вопрос правильного проектирования модели транзакций, чтобы в процесс реализации транзакционно отслеживаемых действий были включены нужные операции - из совершенно другой оперы.
И где тут про исключения? |
Комментарии доступны только авторизованным пользователям, авторизуйтесь или зарегистрируйтесь на сайте здесь
|