Сергей Губанов wrote:
> P>Так ведь если запись в журнал транзакций не прошла, состояние > объекта "ЛокальныйСчет" восстановить требуется. Кто этим займется? > Ага, а удалённый банк надо попросить: "Пожалуйста, верните деньги > обратно"...
Да, как ни странно. Это буква "A"(Atomicity) в слове ACID.
Гениально! Эдак вы скоро и до exception-ов додумаетесь, колллега!
Как только осознаете, наконец, что служебный код как правило не знает, является ли неудача его выполнения критическим багом или нет для данного случая.
Сергей Губанов wrote:
> S>Вот в приведенном примере — что делать, если журнал не смог записать > успешную запись в файл? > Что делать кому? Объекту "ЛокальныйСчёт" по этому поводу точно делать > ни чего не надо, он свою задачу выполнил на 100%, а уж как там внутри > объекта "Журнал" чего-то стряслось — не его дело.
Нет, это как раз его дело. Так как он должен откатить транзакцию и
передать "выше" сообщение об ошибке.
Иначе возможна ситуация, когда программа как-бы прекрасно работает, но
при этом не ведутся логи.
> Ничего подобного. > 1) Не все процедуры обязаны возвращать булевый признак успешности. > Например, операция рапорта о чём-то произошедшем, ничего не должна > возвращать — это никому не интересно кроме неё самой.
Да? А может быть это интересно вызывающему коду. Например,
Заметим, что код функции "отдать_кучу_бабок", реализации интерфейса
"Reporter" и код, вызывающий функцию "отдать_кучу_бабок", может
находиться в разных местах (в разных модулях, например). Более того,
функция "отдать_кучу_бабок" ничего не знает о "важности" функции
"report" — там может быть и просто запись в лог-файл, а может быть и
отсылка важного письма в налоговую при размере транзакции больше
определенного.
Теперь, у нас в функции "report" произошло катастрофическое событие — но
на ход работы функции "отдать_кучу_бабок" это никак не повлияет. Что
делать?
Конечно, поставить для функции report возвращаемое значение типа
"успех/неуспех".
Но от кода возврата нам мало толку — надо как-то передать более
осмысленную информацию. Решение, казалось бы, простое — делаем в классе
Reporter поле error:
Но опять проблема, теперь класс Reporter нельзя использовать в
реентерабельном или многопоточном коде. Значит, нужно передавать еще
один контекст для ошибок, параллельно с классом Reporter.
после свершившегося начисления, откат назад невозможен. К сожалению, я не знаком с устройством банковских систем и не знаю какой базис примитивных операций там используется.
Здравствуйте, mrozov, Вы писали:
M>Гениально! Эдак вы скоро и до exception-ов додумаетесь, колллега! M>Как только осознаете, наконец, что служебный код как правило не знает, является ли неудача его выполнения критическим багом или нет для данного случая.
Совершенно верно — не знает. Процедура чего-то сделала, о результатах сообщила. А при чём тут exception? Или exception — это единственный или лучший способ сообщить о результатах? Нет, не единственный и не лучший. Тогда при чём тут они...
Здравствуйте, Сергей Губанов, Вы писали:
СГ>Тогда надо использовать другой базис примитивных операций. Ибо в выбранном базисе состоящей всего из одной примитивной операции "Начислить":
СГ>PROCEDURE (this: СчётВУдалённомБанке) Начислить* (сумма: Деньги; откого: ОтКого; VAR err: Errors.List): BOOLEAN, NEW, ABSTRACT;
СГ>после свершившегося начисления, откат назад невозможен.
Да уж... Видимо, действительно только C++ники дошли пока до таких понятий, как "базовая гарантия безопасности исключений" и "сильная гарантия безопасности исключений".
... << RSDN@Home 1.1.4 stable rev. 510>>
SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Здравствуйте, Сергей Губанов, Вы писали:
СГ>Ага, а удалённый банк надо попросить: "Пожалуйста, верните деньги обратно"...
Именно. А как иначе? Дисбаланс получится со всеми вытекающими, если не думать о таких вещах.
P>>Кстати, в действительности там все намного сложнее.
СГ>Ясное дело.
Настолько, что в близких к реальным условиях Ваш пример
Сергей Губанов wrote:
> C>Нет, это как раз его дело. Так как он должен откатить транзакцию и > передать "выше" сообщение об ошибке. > Тогда надо использовать другой базис примитивных операций. Ибо в > выбранном базисе состоящей всего из одной примитивной операции > "Начислить": > PROCEDURE (this: СчётВУдалённомБанке) Начислить* (сумма: Деньги; > откого: ОтКого; VAR err: Errors.List): BOOLEAN, NEW, ABSTRACT; > после свершившегося начисления, откат назад невозможен.
Это почему же? Вполне возможно, просто вводится понятие "транзакция".
Грубо говоря, в начале выполнения блока операций мы каким-то образом
запоминаем текущее состояние системы. Далее делаем нужные нам операции
(причем операции не обязаны знать о транзакционности). В случае ошибки
одной из операций, мы просто возвращаем состояние системы в сохраненное
в начале транзакции ИЛИ помечаем измененные данные как неправильные и
дальше их не используем.
Дальше вполне логичный шаг — делим код на классы по обеспечиваемой
гарантии безопасности:
1. Небезопасный — в случае ошибок будет работать неправильно.
2. Базовая гарантия — в случае ошибок сработает штатно, но данные
системы останутся в ошибочном состоянии.
3. Сильная гарантия — в случае ошибок система вернется в предсказуемое
состояние, допускающее дальнейшее использование.
4. Гарантия отсутствия ошибок — система не может попасть в ошибочное
состояние.
Особенно хорошо вопрос по безопасности исключений исследован в С++, так
как там последствия неправильной обработки обычно намного более видимы,
чем в C# или подобных язках. Есть очень хорошая книжка — "Exceptional C++".
СГ>Совершенно верно — не знает. Процедура чего-то сделала, о результатах сообщила. А при чём тут exception? Или exception — это единственный или лучший способ сообщить о результатах? Нет, не единственный и не лучший. Тогда при чём тут они...
Что не единственный — это да. А вот что не лучший — уже категорически нет.
Хотя бы потому, что с точки зрения ООД, функция должна возвращать результат своего выполнения. Например, строку. Что ей делать, столкнувшись с проблемой, не дающей ей вернуть строку?
Вообще, у вас фатальный баг в понятийном аппарате. Вы почему-то думаете, что исключения преднозначены для сообщений об ошибках. А это всего лишь побочный эффект от главного предназначения исключений. А именно:
Исключения — это стандартный и универсальный механизм, с помощью которого код может делегировать принятие решений коду вызывающему данный. Неважно, о чем мы говорим — о модуле, функции или трех строчках кода — это все равно так.
Можно ли делигировать принятие решения другим способом? Можно, чем-то вроде делегатов/событий, но этот метод заметно сложнее в использовании и провоцирует на доп. ошибки (хотя для ряда сценариев и мощнее). Поэтому используются исключения. У них есть свои недостатки, но так как они незначительны, то и разговаривать особо не о чем.
А в тех случаях, когда требуется продолжение работы того же блока кода после сбоя — можно и нужно применять те же события. Никто этому не припятствует. Таким образом мы доказали, что исключения есть полезный и даже необходимый интсрумент при разработке программ.
Здравствуйте, mrozov, Вы писали:
M>Исключения — это стандартный и универсальный механизм, с помощью которого код может делегировать принятие решений коду вызывающему данный. Неважно, о чем мы говорим — о модуле, функции или трех строчках кода — это все равно так.
Причем не важно, сигнализирует ли исключение об ошибке, исключительной ситуации или же исключения используются для реализации логики (бывают и такие ситуации, когда исключения удобнее чем if/elseif/else или switch/case).
... << RSDN@Home 1.1.4 stable rev. 510>>
SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Дарней,
> ПК>Более того, для этой частной задачи реализовать это добро "ручками" совершенно несложно и недолго. > > несложно и недолго — если компилятор нормальный
Несложно и недолго начиная с VC++ 6 SP4 или GCC 2.95. Вы используете что-нибудь более старое?
Posted via RSDN NNTP Server 2.0 beta
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Здравствуйте, mrozov, Вы писали:
M>Исключения — это стандартный и универсальный механизм, с помощью которого код может делегировать принятие решений коду вызывающему данный. Неважно, о чем мы говорим — о модуле, функции или трех строчках кода — это все равно так.
M>Можно ли делигировать принятие решения другим способом?
Можно. Самый распространённый механизм — это возврат некого значения.
Здравствуйте, mrozov, Вы писали:
M>Хотя бы потому, что с точки зрения ООД, функция должна возвращать результат своего выполнения. Например, строку. Что ей делать, столкнувшись с проблемой, не дающей ей вернуть строку?
Если заранее известно, что функция может сталкиваться с подобными проблемами, то это однозначно говорит о том, что функция не должна возвращать строку вовсе:
PROCEDURE TryReturnString (OUT s: String): BOOLEAN;
M>Вообще, у вас фатальный баг в понятийном аппарате...........Таким образом мы доказали, что исключения есть полезный и даже необходимый интсрумент при разработке программ.
Прикольное доказательство
Мой понятийный аппарат, говорит мне, что лучше писать так:
Ну, во-первых, еще раз — есть понятие функции. Оно не предусматривает иных возвращаемых значений, помимо retval. И это удобно. Точно также, как и использование свойства.
Т.е. если вы предлагаете отказаться от конструкций вида:
funct1().func2(someParam).Doit()
то я спрашиваю — а ради чего? Где эта святая цель? Она должна быть дьявольски велика, чтобы я ее принял.
А во-вторых, пример надуманный. Т.е. да, так бывает. Но гораздо чаще бывает так:
try
{
делаем что-то
делаем еще что-то
вообще, нам тут много чего нужно делать
}
catch(Exception1)
{
Делаем что-то полезное при возникновении проблемы №1
}
catch(Exception2)
{
Делаем что-то полезное при возникновении проблемы №2
}
И если вы предлагаете везде проверять статусы и плодить доп. проверки... ну... очень плохо — для вас и вашего работодателя.
Замечу — не использовать исключение, если они доступны, легко. А вот эмулировать их там, где их нет — намного сложнее.
Но главное — да приведите же наконец хоть одну причину, по которой от исключений нужно отказаться. Пока я вижу только ... ну не очень впечатляющие доводы в пользу того, что без них можно обойтись, если все делать через одно место.
Здравствуйте, Cyberax, Вы писали:
>> Тогда надо использовать другой базис примитивных операций. Ибо в >> выбранном базисе состоящей всего из одной примитивной операции >> "Начислить": >> PROCEDURE (this: СчётВУдалённомБанке) Начислить* (сумма: Деньги; откого: ОтКого; VAR err: Errors.List): BOOLEAN, NEW, ABSTRACT; >> после свершившегося начисления, откат назад невозможен.
C>Это почему же?
Деньги уже начисленные на счёт в другом банке??????????
По той же самой причине, что:
1) "Фарш невозможно провернуть назад" (с) С. Лукьяненко.
2) Нельзя распечатать на принтере документ обратно (в зад, так сказать — в чистый лист, и в краску в картиридже).
3) Поезд уже ушел.
Чтобы был возможен откат, базис примитивных операций должен быть другой, ибо операция "Начислить" — необратима.
Здравствуйте, mrozov, Вы писали:
M>Но главное — да приведите же наконец хоть одну причину, по которой от исключений нужно отказаться.
Признаюсь, едва ли мне по силам найти хоть одну причину убедившую бы Вас. Впрочем, и Вам тоже едва ли по силам найти серьёзную причину по которой исключения должны быть встроены в язык, а не в библиотеку.
Здравствуйте, Сергей Губанов, Вы писали:
СГ>Здравствуйте, Cyberax, Вы писали:
>>> Тогда надо использовать другой базис примитивных операций. Ибо в >>> выбранном базисе состоящей всего из одной примитивной операции >>> "Начислить": >>> PROCEDURE (this: СчётВУдалённомБанке) Начислить* (сумма: Деньги; откого: ОтКого; VAR err: Errors.List): BOOLEAN, NEW, ABSTRACT; >>> после свершившегося начисления, откат назад невозможен.
C>>Это почему же?
СГ>Деньги уже начисленные на счёт в другом банке?????????? СГ>По той же самой причине, что: СГ>1) "Фарш невозможно провернуть назад" (с) С. Лукьяненко. СГ>2) Нельзя распечатать на принтере документ обратно (в зад, так сказать — в чистый лист, и в краску в картиридже). СГ>3) Поезд уже ушел.
СГ>Чтобы был возможен откат, базис примитивных операций должен быть другой, ибо операция "Начислить" — необратима.
Простите. вы вообще имели когда-нибудь дело с платежными системами? Я вот да.
Здравствуйте, Сергей Губанов, Вы писали:
СГ>Деньги уже начисленные на счёт в другом банке?????????? СГ>По той же самой причине, что: СГ>1) "Фарш невозможно провернуть назад" (с) С. Лукьяненко. СГ>2) Нельзя распечатать на принтере документ обратно (в зад, так сказать — в чистый лист, и в краску в картиридже). СГ>3) Поезд уже ушел.
.
СГ>Чтобы был возможен откат, базис примитивных операций должен быть другой, ибо операция "Начислить" — необратима.
Даже если операция "Начислить" необратима, как нам узнать про ее неудачное завершение?
Ведь могут быть и другие способы восстановить баланс между банками. Важно только узнать, что в нашем банке операция не была зафиксирована так, как нужно.
... << RSDN@Home 1.1.4 stable rev. 510>>
SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Здравствуйте, Сергей Губанов, Вы писали:
СГ>Здравствуйте, Cyberax, Вы писали:
>>> Тогда надо использовать другой базис примитивных операций. Ибо в >>> выбранном базисе состоящей всего из одной примитивной операции >>> "Начислить": >>> PROCEDURE (this: СчётВУдалённомБанке) Начислить* (сумма: Деньги; откого: ОтКого; VAR err: Errors.List): BOOLEAN, NEW, ABSTRACT; >>> после свершившегося начисления, откат назад невозможен.
C>>Это почему же?
СГ>Деньги уже начисленные на счёт в другом банке?????????? СГ>По той же самой причине, что: СГ>1) "Фарш невозможно провернуть назад" (с) С. Лукьяненко. СГ>2) Нельзя распечатать на принтере документ обратно (в зад, так сказать — в чистый лист, и в краску в картиридже). СГ>3) Поезд уже ушел.
СГ>Чтобы был возможен откат, базис примитивных операций должен быть другой, ибо операция "Начислить" — необратима.
Обратима.
Операция "начислить" — это транзакция, которая в случае успеха фиксируется (commit), а в случае неудачи откатывается (rollback). Только после фиксации деньги фактически зачислены на счёт. И этот принцип называется ACID (см. предыдущие сообщения) и на нём строится вся банковская система со времён не то что бы изобретения первых ЭВМ, а с ранних времён ростовщичества в доверенностях и записях купли/продажи.