Re[15]: Что вы предлагаете на замену эксепшенов?
От: Cyberax Марс  
Дата: 23.11.05 12:34
Оценка:
Сергей Губанов wrote:

> P>Так ведь если запись в журнал транзакций не прошла, состояние

> объекта "ЛокальныйСчет" восстановить требуется. Кто этим займется?
> Ага, а удалённый банк надо попросить: "Пожалуйста, верните деньги
> обратно"...

Да, как ни странно. Это буква "A"(Atomicity) в слове ACID.

--
С уважением,
Alex Besogonov (alexy@izh.com)
Posted via RSDN NNTP Server 1.9
Sapienti sat!
Re[13]: Что вы предлагаете на замену эксепшенов?
От: mrozov  
Дата: 23.11.05 12:48
Оценка: 7 (2) +2 :)))
Здравствуйте, Сергей Губанов, Вы писали:

Гениально! Эдак вы скоро и до exception-ов додумаетесь, колллега!
Как только осознаете, наконец, что служебный код как правило не знает, является ли неудача его выполнения критическим багом или нет для данного случая.
Re[13]: Что вы предлагаете на замену эксепшенов?
От: Cyberax Марс  
Дата: 23.11.05 12:56
Оценка: +1 :)
Сергей Губанов wrote:

> S>Вот в приведенном примере — что делать, если журнал не смог записать

> успешную запись в файл?
> Что делать кому? Объекту "ЛокальныйСчёт" по этому поводу точно делать
> ни чего не надо, он свою задачу выполнил на 100%, а уж как там внутри
> объекта "Журнал" чего-то стряслось — не его дело.

Нет, это как раз его дело. Так как он должен откатить транзакцию и
передать "выше" сообщение об ошибке.

Иначе возможна ситуация, когда программа как-бы прекрасно работает, но
при этом не ведутся логи.

> Ничего подобного.

> 1) Не все процедуры обязаны возвращать булевый признак успешности.
> Например, операция рапорта о чём-то произошедшем, ничего не должна
> возвращать — это никому не интересно кроме неё самой.

Да? А может быть это интересно вызывающему коду. Например,
class Reporter
{
public:
    virtual void report(...)=0;
};

void отдать_кучу_бабок(Reporter *reporter, ...)
{
    //переводим кучу бабок
    reporter->report(отчет_о_переводе_кучи_бабок);
}

Заметим, что код функции "отдать_кучу_бабок", реализации интерфейса
"Reporter" и код, вызывающий функцию "отдать_кучу_бабок", может
находиться в разных местах (в разных модулях, например). Более того,
функция "отдать_кучу_бабок" ничего не знает о "важности" функции
"report" — там может быть и просто запись в лог-файл, а может быть и
отсылка важного письма в налоговую при размере транзакции больше
определенного.

Теперь, у нас в функции "report" произошло катастрофическое событие — но
на ход работы функции "отдать_кучу_бабок" это никак не повлияет. Что
делать?

Конечно, поставить для функции report возвращаемое значение типа
"успех/неуспех".
class Reporter
{
public:
    bool void report(...)=0;
};

bool отдать_кучу_бабок(Reporter *reporter, ...)
{
    //переводим кучу бабок
    if (!reporter->report(отчет_о_переводе_кучи_бабок)) return false;
    ...
    return true;
}


Но от кода возврата нам мало толку — надо как-то передать более
осмысленную информацию. Решение, казалось бы, простое — делаем в классе
Reporter поле error:
class Reporter
{
public:
    Error *error;
    bool void report(...)=0;
};

bool отдать_кучу_бабок(Reporter *reporter, ...)
{
    //переводим кучу бабок
    if (!reporter->report(отчет_о_переводе_кучи_бабок))
    {
        //Откатить транзакцию, выполнить cleanup
        return false;
    }
    ...
    return true;
}


Но опять проблема, теперь класс Reporter нельзя использовать в
реентерабельном или многопоточном коде. Значит, нужно передавать еще
один контекст для ошибок, параллельно с классом Reporter.

class Reporter
{
public:
    bool void report(...)=0;
};

class ErrorContext
{
public:
        Error *error;
};

bool отдать_кучу_бабок(Reporter *reporter, ErrorContext *ctx,...)
{
    //переводим кучу бабок
    if (!reporter->report(отчет_о_переводе_кучи_бабок))
    {
        //Откатить транзакцию, выполнить cleanup
        return false;
    }
    ...
    return true;
}


Теперь осталось написать вызывающий код:
...
ErrorContext ctx;
Reporter *rep = .... ;
отдать_кучу_бабок(rep,&ctx,....);

if (ctx.error) //Случилась беда?
{
    //Да, все плохо :(
    if (std::typeid(ctx.error)==std::typeif(ErrorEmailFailed))
       //Делаем MessageBox с сообщением "не удалось послать письмо"
    else if (std::typeid(ctx.error)==std::typeif(ErrorDiskFull))
       //....
}
...


Теперь осталось посмотреть на это все пристальным взглядом....

--
С уважением,
Alex Besogonov (alexy@izh.com)
Posted via RSDN NNTP Server 1.9
Sapienti sat!
Re[14]: Что вы предлагаете на замену эксепшенов?
От: Сергей Губанов Россия http://sergey-gubanov.livejournal.com/
Дата: 23.11.05 13:36
Оценка:
Здравствуйте, Cyberax, Вы писали:

C>Нет, это как раз его дело. Так как он должен откатить транзакцию и передать "выше" сообщение об ошибке.


Тогда надо использовать другой базис примитивных операций. Ибо в выбранном базисе состоящей всего из одной примитивной операции "Начислить":

PROCEDURE (this: СчётВУдалённомБанке) Начислить* (сумма: Деньги; откого: ОтКого; VAR err: Errors.List): BOOLEAN, NEW, ABSTRACT;

после свершившегося начисления, откат назад невозможен. К сожалению, я не знаком с устройством банковских систем и не знаю какой базис примитивных операций там используется.
Re[14]: Что вы предлагаете на замену эксепшенов?
От: Сергей Губанов Россия http://sergey-gubanov.livejournal.com/
Дата: 23.11.05 13:44
Оценка:
Здравствуйте, mrozov, Вы писали:

M>Гениально! Эдак вы скоро и до exception-ов додумаетесь, колллега!

M>Как только осознаете, наконец, что служебный код как правило не знает, является ли неудача его выполнения критическим багом или нет для данного случая.

Совершенно верно — не знает. Процедура чего-то сделала, о результатах сообщила. А при чём тут exception? Или exception — это единственный или лучший способ сообщить о результатах? Нет, не единственный и не лучший. Тогда при чём тут они...
Re[15]: Что вы предлагаете на замену эксепшенов?
От: eao197 Беларусь http://eao197.blogspot.com
Дата: 23.11.05 13:50
Оценка: +2
Здравствуйте, Сергей Губанов, Вы писали:

СГ>Тогда надо использовать другой базис примитивных операций. Ибо в выбранном базисе состоящей всего из одной примитивной операции "Начислить":


СГ>PROCEDURE (this: СчётВУдалённомБанке) Начислить* (сумма: Деньги; откого: ОтКого; VAR err: Errors.List): BOOLEAN, NEW, ABSTRACT;


СГ>после свершившегося начисления, откат назад невозможен.


Да уж... Видимо, действительно только C++ники дошли пока до таких понятий, как "базовая гарантия безопасности исключений" и "сильная гарантия безопасности исключений".
... << RSDN@Home 1.1.4 stable rev. 510>>


SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Re[15]: Что вы предлагаете на замену эксепшенов?
От: Privalov  
Дата: 23.11.05 13:52
Оценка:
Здравствуйте, Сергей Губанов, Вы писали:

СГ>Ага, а удалённый банк надо попросить: "Пожалуйста, верните деньги обратно"...


Именно. А как иначе? Дисбаланс получится со всеми вытекающими, если не думать о таких вещах.

P>>Кстати, в действительности там все намного сложнее.


СГ>Ясное дело.


Настолько, что в близких к реальным условиях Ваш пример
Автор: Сергей Губанов
Дата: 22.11.05
неработоспособен.
Re[15]: Что вы предлагаете на замену эксепшенов?
От: Cyberax Марс  
Дата: 23.11.05 14:00
Оценка: 2 (2) +2
Сергей Губанов wrote:

> C>Нет, это как раз его дело. Так как он должен откатить транзакцию и

> передать "выше" сообщение об ошибке.
> Тогда надо использовать другой базис примитивных операций. Ибо в
> выбранном базисе состоящей всего из одной примитивной операции
> "Начислить":
> PROCEDURE (this: СчётВУдалённомБанке) Начислить* (сумма: Деньги;
> откого: ОтКого; VAR err: Errors.List): BOOLEAN, NEW, ABSTRACT;
> после свершившегося начисления, откат назад невозможен.

Это почему же? Вполне возможно, просто вводится понятие "транзакция".

Грубо говоря, в начале выполнения блока операций мы каким-то образом
запоминаем текущее состояние системы. Далее делаем нужные нам операции
(причем операции не обязаны знать о транзакционности). В случае ошибки
одной из операций, мы просто возвращаем состояние системы в сохраненное
в начале транзакции ИЛИ помечаем измененные данные как неправильные и
дальше их не используем.

Дальше вполне логичный шаг — делим код на классы по обеспечиваемой
гарантии безопасности:
1. Небезопасный — в случае ошибок будет работать неправильно.
2. Базовая гарантия — в случае ошибок сработает штатно, но данные
системы останутся в ошибочном состоянии.
3. Сильная гарантия — в случае ошибок система вернется в предсказуемое
состояние, допускающее дальнейшее использование.
4. Гарантия отсутствия ошибок — система не может попасть в ошибочное
состояние.

Особенно хорошо вопрос по безопасности исключений исследован в С++, так
как там последствия неправильной обработки обычно намного более видимы,
чем в C# или подобных язках. Есть очень хорошая книжка — "Exceptional C++".

--
С уважением,
Alex Besogonov (alexy@izh.com)
Posted via RSDN NNTP Server 1.9
Sapienti sat!
Re[15]: Что вы предлагаете на замену эксепшенов?
От: mrozov  
Дата: 23.11.05 14:14
Оценка: +1
Здравствуйте, Сергей Губанов, Вы писали:


СГ>Совершенно верно — не знает. Процедура чего-то сделала, о результатах сообщила. А при чём тут exception? Или exception — это единственный или лучший способ сообщить о результатах? Нет, не единственный и не лучший. Тогда при чём тут они...


Что не единственный — это да. А вот что не лучший — уже категорически нет.

Хотя бы потому, что с точки зрения ООД, функция должна возвращать результат своего выполнения. Например, строку. Что ей делать, столкнувшись с проблемой, не дающей ей вернуть строку?

Вообще, у вас фатальный баг в понятийном аппарате. Вы почему-то думаете, что исключения преднозначены для сообщений об ошибках. А это всего лишь побочный эффект от главного предназначения исключений. А именно:

Исключения — это стандартный и универсальный механизм, с помощью которого код может делегировать принятие решений коду вызывающему данный. Неважно, о чем мы говорим — о модуле, функции или трех строчках кода — это все равно так.

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

А в тех случаях, когда требуется продолжение работы того же блока кода после сбоя — можно и нужно применять те же события. Никто этому не припятствует. Таким образом мы доказали, что исключения есть полезный и даже необходимый интсрумент при разработке программ.
Re[16]: Что вы предлагаете на замену эксепшенов?
От: eao197 Беларусь http://eao197.blogspot.com
Дата: 23.11.05 14:23
Оценка:
Здравствуйте, mrozov, Вы писали:

M>Исключения — это стандартный и универсальный механизм, с помощью которого код может делегировать принятие решений коду вызывающему данный. Неважно, о чем мы говорим — о модуле, функции или трех строчках кода — это все равно так.


Причем не важно, сигнализирует ли исключение об ошибке, исключительной ситуации или же исключения используются для реализации логики (бывают и такие ситуации, когда исключения удобнее чем if/elseif/else или switch/case).
... << RSDN@Home 1.1.4 stable rev. 510>>


SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Re[6]: Что вы предлагаете на замену эксепшенов?
От: Павел Кузнецов  
Дата: 23.11.05 14:28
Оценка:
Дарней,

> ПК>Более того, для этой частной задачи реализовать это добро "ручками" совершенно несложно и недолго.

>
> несложно и недолго — если компилятор нормальный

Несложно и недолго начиная с VC++ 6 SP4 или GCC 2.95. Вы используете что-нибудь более старое?
Posted via RSDN NNTP Server 2.0 beta
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Re[16]: Что вы предлагаете на замену эксепшенов?
От: Andrei N.Sobchuck Украина www.smalltalk.ru
Дата: 23.11.05 14:33
Оценка:
Здравствуйте, mrozov, Вы писали:

M>Исключения — это стандартный и универсальный механизм, с помощью которого код может делегировать принятие решений коду вызывающему данный. Неважно, о чем мы говорим — о модуле, функции или трех строчках кода — это все равно так.


M>Можно ли делигировать принятие решения другим способом?


Можно. Самый распространённый механизм — это возврат некого значения.
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Я ненавижу Hibernate
Автор: Andrei N.Sobchuck
Дата: 08.01.08
!
Re[16]: Что вы предлагаете на замену эксепшенов?
От: Сергей Губанов Россия http://sergey-gubanov.livejournal.com/
Дата: 23.11.05 14:41
Оценка: -1
Здравствуйте, mrozov, Вы писали:

M>Хотя бы потому, что с точки зрения ООД, функция должна возвращать результат своего выполнения. Например, строку. Что ей делать, столкнувшись с проблемой, не дающей ей вернуть строку?


Если заранее известно, что функция может сталкиваться с подобными проблемами, то это однозначно говорит о том, что функция не должна возвращать строку вовсе:

PROCEDURE TryReturnString (OUT s: String): BOOLEAN;

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


Прикольное доказательство

Мой понятийный аппарат, говорит мне, что лучше писать так:
  IF TryReturnString(str) THEN 
    ... 
  ELSE 
    ... 
  END

чем вот так:
  bool valid = false;
  try
  {
    str = ReturnString();
    valid = true;
  }
  catch
  {
    ......
  }
  if (valid)
  {
     ........
  }

и я рассматриваю второй вариант как фатальный баг в понятийном аппарате.
Re[17]: Что вы предлагаете на замену эксепшенов?
От: eao197 Беларусь http://eao197.blogspot.com
Дата: 23.11.05 14:47
Оценка:
Здравствуйте, Сергей Губанов, Вы писали:


СГ>вот так:

СГ>
СГ>  bool valid = false;
СГ>  try
СГ>  {
СГ>    str = ReturnString();
СГ>    valid = true;
СГ>  }
СГ>  catch
СГ>  {
СГ>    ......
СГ>  }
СГ>  if (valid)
СГ>  {
СГ>     ........
СГ>  }
СГ>


Это уж действительно баг в понятийном аппарате
Поскольку проще написать так:
try
    {
        str = ReturnString();
        useStringAsYouWant();
    }
catch( const std::exception & x )
    {
        allGoingToForest();
    }
... << RSDN@Home 1.1.4 stable rev. 510>>


SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Re[17]: Что вы предлагаете на замену эксепшенов?
От: mrozov  
Дата: 23.11.05 14:51
Оценка:
Ну, во-первых, еще раз — есть понятие функции. Оно не предусматривает иных возвращаемых значений, помимо retval. И это удобно. Точно также, как и использование свойства.

Т.е. если вы предлагаете отказаться от конструкций вида:
funct1().func2(someParam).Doit()
то я спрашиваю — а ради чего? Где эта святая цель? Она должна быть дьявольски велика, чтобы я ее принял.

А во-вторых, пример надуманный. Т.е. да, так бывает. Но гораздо чаще бывает так:

try
{
делаем что-то
делаем еще что-то
вообще, нам тут много чего нужно делать
}
catch(Exception1)
{
Делаем что-то полезное при возникновении проблемы №1
}
catch(Exception2)
{
Делаем что-то полезное при возникновении проблемы №2
}

И если вы предлагаете везде проверять статусы и плодить доп. проверки... ну... очень плохо — для вас и вашего работодателя.

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

Но главное — да приведите же наконец хоть одну причину, по которой от исключений нужно отказаться. Пока я вижу только ... ну не очень впечатляющие доводы в пользу того, что без них можно обойтись, если все делать через одно место.
Re[16]: Что вы предлагаете на замену эксепшенов?
От: Сергей Губанов Россия http://sergey-gubanov.livejournal.com/
Дата: 23.11.05 14:54
Оценка:
Здравствуйте, Cyberax, Вы писали:

>> Тогда надо использовать другой базис примитивных операций. Ибо в

>> выбранном базисе состоящей всего из одной примитивной операции
>> "Начислить":
>> PROCEDURE (this: СчётВУдалённомБанке) Начислить* (сумма: Деньги; откого: ОтКого; VAR err: Errors.List): BOOLEAN, NEW, ABSTRACT;
>> после свершившегося начисления, откат назад невозможен.

C>Это почему же?


Деньги уже начисленные на счёт в другом банке??????????
По той же самой причине, что:
1) "Фарш невозможно провернуть назад" (с) С. Лукьяненко.
2) Нельзя распечатать на принтере документ обратно (в зад, так сказать — в чистый лист, и в краску в картиридже).
3) Поезд уже ушел.

Чтобы был возможен откат, базис примитивных операций должен быть другой, ибо операция "Начислить" — необратима.
Re[18]: Что вы предлагаете на замену эксепшенов?
От: Сергей Губанов Россия http://sergey-gubanov.livejournal.com/
Дата: 23.11.05 14:59
Оценка:
Здравствуйте, mrozov, Вы писали:

M>Но главное — да приведите же наконец хоть одну причину, по которой от исключений нужно отказаться.


Признаюсь, едва ли мне по силам найти хоть одну причину убедившую бы Вас. Впрочем, и Вам тоже едва ли по силам найти серьёзную причину по которой исключения должны быть встроены в язык, а не в библиотеку.
Re[17]: Что вы предлагаете на замену эксепшенов?
От: mrozov  
Дата: 23.11.05 15:02
Оценка:
Здравствуйте, Сергей Губанов, Вы писали:

СГ>Здравствуйте, Cyberax, Вы писали:


>>> Тогда надо использовать другой базис примитивных операций. Ибо в

>>> выбранном базисе состоящей всего из одной примитивной операции
>>> "Начислить":
>>> PROCEDURE (this: СчётВУдалённомБанке) Начислить* (сумма: Деньги; откого: ОтКого; VAR err: Errors.List): BOOLEAN, NEW, ABSTRACT;
>>> после свершившегося начисления, откат назад невозможен.

C>>Это почему же?


СГ>Деньги уже начисленные на счёт в другом банке??????????

СГ>По той же самой причине, что:
СГ>1) "Фарш невозможно провернуть назад" (с) С. Лукьяненко.
СГ>2) Нельзя распечатать на принтере документ обратно (в зад, так сказать — в чистый лист, и в краску в картиридже).
СГ>3) Поезд уже ушел.

СГ>Чтобы был возможен откат, базис примитивных операций должен быть другой, ибо операция "Начислить" — необратима.


Простите. вы вообще имели когда-нибудь дело с платежными системами? Я вот да.
Re[17]: Что вы предлагаете на замену эксепшенов?
От: eao197 Беларусь http://eao197.blogspot.com
Дата: 23.11.05 15:03
Оценка:
Здравствуйте, Сергей Губанов, Вы писали:

СГ>Деньги уже начисленные на счёт в другом банке??????????

СГ>По той же самой причине, что:
СГ>1) "Фарш невозможно провернуть назад" (с) С. Лукьяненко.
СГ>2) Нельзя распечатать на принтере документ обратно (в зад, так сказать — в чистый лист, и в краску в картиридже).
СГ>3) Поезд уже ушел.

В исходном задании не было сказано про удаленный банк: Re[7]: Что вы предлагаете на замену эксепшенов?
Автор: Sinclair
Дата: 22.11.05
.

СГ>Чтобы был возможен откат, базис примитивных операций должен быть другой, ибо операция "Начислить" — необратима.


Даже если операция "Начислить" необратима, как нам узнать про ее неудачное завершение?
Ведь могут быть и другие способы восстановить баланс между банками. Важно только узнать, что в нашем банке операция не была зафиксирована так, как нужно.
... << RSDN@Home 1.1.4 stable rev. 510>>


SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Re[17]: Что вы предлагаете на замену эксепшенов?
От: iZEN СССР  
Дата: 23.11.05 15:10
Оценка:
Здравствуйте, Сергей Губанов, Вы писали:

СГ>Здравствуйте, Cyberax, Вы писали:


>>> Тогда надо использовать другой базис примитивных операций. Ибо в

>>> выбранном базисе состоящей всего из одной примитивной операции
>>> "Начислить":
>>> PROCEDURE (this: СчётВУдалённомБанке) Начислить* (сумма: Деньги; откого: ОтКого; VAR err: Errors.List): BOOLEAN, NEW, ABSTRACT;
>>> после свершившегося начисления, откат назад невозможен.

C>>Это почему же?


СГ>Деньги уже начисленные на счёт в другом банке??????????

СГ>По той же самой причине, что:
СГ>1) "Фарш невозможно провернуть назад" (с) С. Лукьяненко.
СГ>2) Нельзя распечатать на принтере документ обратно (в зад, так сказать — в чистый лист, и в краску в картиридже).
СГ>3) Поезд уже ушел.

СГ>Чтобы был возможен откат, базис примитивных операций должен быть другой, ибо операция "Начислить" — необратима.

Обратима.
Операция "начислить" — это транзакция, которая в случае успеха фиксируется (commit), а в случае неудачи откатывается (rollback). Только после фиксации деньги фактически зачислены на счёт. И этот принцип называется ACID (см. предыдущие сообщения) и на нём строится вся банковская система со времён не то что бы изобретения первых ЭВМ, а с ранних времён ростовщичества в доверенностях и записях купли/продажи.
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.