Re[24]: –
От: samius Япония http://sams-tricks.blogspot.com
Дата: 17.08.10 19:04
Оценка:
Здравствуйте, mrTwister, Вы писали:

T>>>Да легко, можно было бы сделать так, что если данные на форме валидны, то ничего не делать, а если невалидны, то кидать ValidationException. Клиент поймал бы это исключение, и нарисовал красиво какие именно контролы невалидны. Но разработчики Enterprise Library вполне обоснованно не стали так делать, так как пользователю пришлось бы для валидации писать try/catch. Вообще, исключения надо использовать там, где предполагается, что клиент кода будет не ловить эти исключения, а пробрасывать выше. Если мы предполагаем, что клиент кода должен обработать исключение и в соответствии с этим исключением выполнить ту или иную логику, значит лучше вместо исключений использовать коды возврата.


S>>Одно дело валидация контролов на форме, другое — прогнозирование результата операции.


T>При чем тут прогнозирование?


Притом что мы с midcyber обсуждали следующий кусок кода

void Usage()
{
  if(user.CanCompleteOperation(op))
   user.DoOperation(op);
  else
   gui.SetText(user.GetLocalizedError(op));
}


S>>А от проблем с сетью и сервером приложений/БД транзакции защитят? Или транзакции сами не выкидывают исключений?


T>Проблемы с сетью и пр. имеют мало общего с бизнес-логикой и вполне логично, что обрабатываться они должны совсем иначе. Мы же обсуждаем сейчас бизнес-логику, не так ли?


В контексте процитированного кода тот код, которому достанется исключение, даже не узнает, была ли начата операция или нет.
Хоть проблемы с сетью и прочим имеют другой уровень, но они довольно тесно связаны с бизнес-логикой. Блокировать ли деятельность пользователя до появления сети, предоставлять ли ему доступ к локальным фичам — это решается в контексте производимой операции и случившейся проблемы. А мы тут даже не знаем, на проверке срезало, на операции, или на доставании кода ошибки.
Re[13]: Исключения для пользователя и для программиста -- ра
От: fmiracle  
Дата: 17.08.10 20:27
Оценка:
Здравствуйте, 0K, Вы писали:

0K>Какого вида UI -- не имеет значения. Информация об ошибке с UI никак не связана (кроме локализации, но локализацию по правилам нужно делать для всех используемых языков, и это не мое требование).


Во-первых, я (читай — пользователь/заказчик) хочу, например, в своем веб приложении вывести пользователю сообщение об ошибке примерно так:

Внимание! Сервер maps.yandex.ru не отвечает!


Как в строковом сообщении об исключении должно быть выделено название сервера? А если оно там не будет выделено, то нафига мне такое готовое сообщение нужно?

Во-вторы, как тебе уже не раз говорили, что вот такое сообщение пользователю:

Файл базы телефонных номеров не найден. Создать новый? (Ok) (Cancel)


Выглядит (для пользователя, как умного так и не очень) гораздо лучше чем вот такое:

Отсутствует файл c:\users\documents and settins\appdata\sandbox\myapp.dat.


Опять же — сообщение снова не становится особенно важным, а важны детали в объекте исключения.


Твой вариант с "Давайте сделаем такие исключения, чтобы их можно было сразу показывать пользвоателю без обработки" полностью соответствует известному анекдоту:

- Это патентное бюро? Я изобрел уникальный автоматический аппарат для бритья!
— О, это интересно, как работает?
— Ну вот опускаете сюда лицо и автоматические лезвия быстро и чисто бреют.
— Хммм. Но постойте, ведь у каждого человека уникальная форма лица?!
— Ну, это только на первый раз.



Тебе тут все пытаются объяснить, что Message в исключении придуман не для показа пользователю, а для общей информации разработчику, чтобы быстрее понять в целом что за проблема (основная же информция — в типе исключения и его полях). Показ Message сообщения пользователю напряму — это неправильное использование придуманного механизма.
Потому твое предложение выглядит странно и забавно. Как что-то вроде жалобы пользователя "Я пытался забивать отверткой гвозди, но получилось плохо. Почему до сих пор никто не выпускает ответок с утяжеленными рукоятками? Тогда ведь гвозди забивались бы ими гораздо лучше!".
... << RSDN@Home 1.2.0 alpha 4 rev. 1237>>
Re[25]: –
От: mrTwister Россия  
Дата: 21.08.10 10:20
Оценка:
Здравствуйте, samius, Вы писали:

S>В контексте процитированного кода тот код, которому достанется исключение, даже не узнает, была ли начата операция или нет.

S>Хоть проблемы с сетью и прочим имеют другой уровень, но они довольно тесно связаны с бизнес-логикой. Блокировать ли деятельность пользователя до появления сети, предоставлять ли ему доступ к локальным фичам — это решается в контексте производимой операции и случившейся проблемы.

Значит в данном случае, это часть бизнес-логики

S>А мы тут даже не знаем, на проверке срезало, на операции, или на доставании кода ошибки.


Почему не знаем?
В OperationResult — мы можем поместить любую информацию, какую сочтем нужным
лэт ми спик фром май харт
Re[26]: –
От: samius Япония http://sams-tricks.blogspot.com
Дата: 21.08.10 11:29
Оценка:
Здравствуйте, mrTwister, Вы писали:

T>Здравствуйте, samius, Вы писали:


S>>А мы тут даже не знаем, на проверке срезало, на операции, или на доставании кода ошибки.


T>Почему не знаем?

Потому что обсуждаемый код не возвращает вызывающему такой информации.

T>В OperationResult — мы можем поместить любую информацию, какую сочтем нужным

Можем, только это уже будет совсем другой код.
Re[27]: –
От: mrTwister Россия  
Дата: 21.08.10 11:57
Оценка:
Здравствуйте, samius, Вы писали:


T>>Почему не знаем?

S>Потому что обсуждаемый код не возвращает вызывающему такой информации.

Что ты зациклился на том примере. Если тебе паттерн tester-doer не подходит, то это не значит, что тебе надо использовать исключения. Альтернативный вариант я привел.
лэт ми спик фром май харт
Re[28]: –
От: samius Япония http://sams-tricks.blogspot.com
Дата: 21.08.10 12:46
Оценка:
Здравствуйте, mrTwister, Вы писали:

T>Здравствуйте, samius, Вы писали:



T>>>Почему не знаем?

S>>Потому что обсуждаемый код не возвращает вызывающему такой информации.

T>Что ты зациклился на том примере. Если тебе паттерн tester-doer не подходит, то это не значит, что тебе надо использовать исключения.

Конечно не значит. Но при всем богатсве выбора я склонен использовать исключения.

T>Альтернативный вариант я привел.

Ты привел ссылку на Validator.Validate. Давай хотя бы псевдокод альтернативного варианта в студию.
Re[29]: –
От: mrTwister Россия  
Дата: 21.08.10 20:20
Оценка:
Здравствуйте, samius, Вы писали:

T>>Альтернативный вариант я привел.

S>Ты привел ссылку на Validator.Validate. Давай хотя бы псевдокод альтернативного варианта в студию.


void Usage()
{
    var operationResult = user.TryDoOperation();
    if(operationResult.Success)
    {
        MessageBox.Show("Success");
    }
    else
    {
        MessageBox.Show("Failed. Params: {0}, {1}", operationResult.param1, operationResult.param2);
    }
}


Таким образом, control flow алгоритма выражен явно и чисто. Я не говорю что этот способ универсален. Где-то лучше исключения, а где-то коды возврата.
лэт ми спик фром май харт
Re[30]: –
От: samius Япония http://sams-tricks.blogspot.com
Дата: 22.08.10 06:24
Оценка:
Здравствуйте, mrTwister, Вы писали:

T>Здравствуйте, samius, Вы писали:


T>>>Альтернативный вариант я привел.

S>>Ты привел ссылку на Validator.Validate. Давай хотя бы псевдокод альтернативного варианта в студию.


T>
T>void Usage()
T>{
T>    var operationResult = user.TryDoOperation();
T>    if(operationResult.Success)
T>    {
T>        MessageBox.Show("Success");
T>    }
T>    else
T>    {
T>        MessageBox.Show("Failed. Params: {0}, {1}", operationResult.param1, operationResult.param2);
T>    }
T>}
T>


T>Таким образом, control flow алгоритма выражен явно и чисто.

Для меня в таком явном чистом control flow не очевидно, что будет если внутри TryDoOperation возбудится то или иное исключение.

T>Я не говорю что этот способ универсален. Где-то лучше исключения, а где-то коды возврата.

Давай таки разберемся, где лучше и чем.
В случае с Dictionary.TryGetValue — я с тобой соглашусь, потому как Dictionary отлажен и получить что либо кроме ArgumentNullException практически нереально. При внутри вызова метода не происходит обращений к внешним источникам данных и сервисам, которое может окончится неудачей. В случае с user.TryDoOperation — нет. Это во-первых сфероконь, реализация которого может быть неверна, он скорее всего обращается к внешним сервисам и источникам данных, наличие и состояние которых может быть под вопросом (если речь идет не о крестиках-ноликах).

Твой Usage заставляет меня сделать два взаимоисключающих предположения:
1) TryDoOperation катчит все исключения и пакует их в OperationResult. Тогда я не понимаю, как можно сделать пакетную обработку
foreach(user in users) user.TryDoOperation()
судя по всему придется анализировать результат после каждого пользователя и решать, стоит ли продолжать или какой-то сервис не доступен...
2) TryDoOperation катчит не все исключения. Тогда видимо в случае таких исключений пользователь не увидит MessageBox с надписью "Failed".

А что если все операции над моделью данных возвращают OperationResult? Их же придется комбинировать, а значит в каждой операции анализировать чем окончилась вложенная...

Ты работал плотно с COM из C++, где после каждого чиха нужно проверить HRESULT?
Re[31]: –
От: mrTwister Россия  
Дата: 22.08.10 08:54
Оценка:
Здравствуйте, samius, Вы писали:

T>>
T>>void Usage()
T>>{
T>>    var operationResult = user.TryDoOperation();
T>>    if(operationResult.Success)
T>>    {
T>>        MessageBox.Show("Success");
T>>    }
T>>    else
T>>    {
T>>        MessageBox.Show("Failed. Params: {0}, {1}", operationResult.param1, operationResult.param2);
T>>    }
T>>}
T>>


T>>Таким образом, control flow алгоритма выражен явно и чисто.

S>Для меня в таком явном чистом control flow не очевидно, что будет если внутри TryDoOperation возбудится то или иное исключение.

Если внутри TryDoOperation возбудится то или иное исключение, то это исключение будет проброшено выше и обработано на верхнем уровне с логгированием и показом некоторого обобщенного сообщения об ошибке. Ничего осмысленного внутри Usage() ты все равно сделать не сможешь. Откуда ты знаешь, какое исключение может прилететь из TryDoOperation? Это может быть и OutOfMemoryException, и StackOverflowException и ThreadAbortException. Ты их будешь обрабатывать? Даже если касаться ситуации отсутствия соединения, какие исключения ты собрался ловить? Там могут быть какие угодно исключения. Если у нас на транспортном уровне WCF, то это одни исключения, если Remoting, то другие, если сокеты, то третьи. Более того, сам факт отсутствия соединения может быть сигнализирован целой группой разных исключений. Это и DNS не может имя разрезолвить, и таймаут, и отсутствует листенер на порту, и удаленный сервер закрывает соединение, и протокол не тот, отсутствие сетевых интерфейсов — да мало ли чего? Ты что, собрался в обработчике каждой кнопки перехватывать всю тонну исключений? Или ты будешь перехватывать обобщенный, а потому и неизвестно какой базовый Exception?

T>>Я не говорю что этот способ универсален. Где-то лучше исключения, а где-то коды возврата.

S>Давай таки разберемся, где лучше и чем.
Правило простое. Все зависит от клиента. Если клиент в ответ на результат выполнения операции должен реализовать некоторое определенное поведение, то есть явным образом обработать этот результат, то надо использовать коды возврата. Если же клиент должен просто пробросить ошибку на верхний уровень с надеждой на то, что кто-то там где-то когда-нибудь может быть эту ошибку обработает, то надо использовать исключения.

S>В случае с Dictionary.TryGetValue — я с тобой соглашусь, потому как Dictionary отлажен и получить что либо кроме ArgumentNullException практически нереально. При внутри вызова метода не происходит обращений к внешним источникам данных и сервисам, которое может окончится неудачей.


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

if(!dictionary.ContainsKey(key))
{
    dictionary[key] = value;
}
return dictionary[key];


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

S>В случае с user.TryDoOperation — нет. Это во-первых сфероконь, реализация которого может быть неверна, он скорее всего обращается к внешним сервисам и источникам данных, наличие и состояние которых может быть под вопросом (если речь идет не о крестиках-ноликах).


Ну да, и что?

S>Твой Usage заставляет меня сделать два взаимоисключающих предположения:

S>1) TryDoOperation катчит все исключения и пакует их в OperationResult.
Нет, он точно не перехватывает все исключения. Он перехватывает только те исключения, которые ожидает. Все исключения перехватывать нельзя.

S>Тогда я не понимаю, как можно сделать пакетную обработку

S>foreach(user in users) user.TryDoOperation()
S>судя по всему придется анализировать результат после каждого пользователя и решать, стоит ли продолжать или какой-то сервис не доступен...
S>2) TryDoOperation катчит не все исключения. Тогда видимо в случае таких исключений пользователь не увидит MessageBox с надписью "Failed".
Да, он увидит обобщенное сообщение об ошибки. Это значит, что произошла какая-то непредвиденная ситуация. Например, закончилась память (OutOfMemory), или сгорела сетевая карта, у процесса нехватает прав на обращение к сетевому интерфейсу, либо где-то есть ошибка в коде (например, ArgumentNullException). В этом случае лучшее, что мы можем сделать — это сообщить службе поддержки о проблеме, предоставив ей максимум информации. Следовательно ошибку надо подробно залоггировать, можно послать соответствующий email кому надо, а пользователю показать окно с текстом "Упс, что-то хреновое произошло!"

S>А что если все операции над моделью данных возвращают OperationResult? Их же придется комбинировать, а значит в каждой операции анализировать чем окончилась вложенная...

S>Ты работал плотно с COM из C++, где после каждого чиха нужно проверить HRESULT?
Замечательно, ты наверное, придумал случай, где коды возврата подходят плохо. Тогда я цитирую самого себя:

Я не говорю что этот способ универсален. Где-то лучше исключения, а где-то коды возврата.

лэт ми спик фром май харт
Re[32]: –
От: samius Япония http://sams-tricks.blogspot.com
Дата: 22.08.10 13:38
Оценка:
Здравствуйте, mrTwister, Вы писали:

T>Здравствуйте, samius, Вы писали:


S>>Для меня в таком явном чистом control flow не очевидно, что будет если внутри TryDoOperation возбудится то или иное исключение.


T>Если внутри TryDoOperation возбудится то или иное исключение, то это исключение будет проброшено выше и обработано на верхнем уровне с логгированием и показом некоторого обобщенного сообщения об ошибке. Ничего осмысленного внутри Usage() ты все равно сделать не сможешь. Откуда ты знаешь, какое исключение может прилететь из TryDoOperation? Это может быть и OutOfMemoryException, и StackOverflowException и ThreadAbortException. Ты их будешь обрабатывать? Даже если касаться ситуации отсутствия соединения, какие исключения ты собрался ловить? Там могут быть какие угодно исключения. Если у нас на транспортном уровне WCF, то это одни исключения, если Remoting, то другие, если сокеты, то третьи. Более того, сам факт отсутствия соединения может быть сигнализирован целой группой разных исключений. Это и DNS не может имя разрезолвить, и таймаут, и отсутствует листенер на порту, и удаленный сервер закрывает соединение, и протокол не тот, отсутствие сетевых интерфейсов — да мало ли чего? Ты что, собрался в обработчике каждой кнопки перехватывать всю тонну исключений? Или ты будешь перехватывать обобщенный, а потому и неизвестно какой базовый Exception?

Меня смущает ветка else в твоем коде. При наличии любого исключения пользователь не увидит сообщение "Failed". А значит может думать что операция прошла, чихать что сработал обработчик верхнего уровня и пополнил лог... То есть при том, что ты назадавал мне правильных вопросов, твой Usage будет работать странновато.


T>>>Я не говорю что этот способ универсален. Где-то лучше исключения, а где-то коды возврата.

S>>Давай таки разберемся, где лучше и чем.
T>Правило простое. Все зависит от клиента. Если клиент в ответ на результат выполнения операции должен реализовать некоторое определенное поведение, то есть явным образом обработать этот результат, то надо использовать коды возврата. Если же клиент должен просто пробросить ошибку на верхний уровень с надеждой на то, что кто-то там где-то когда-нибудь может быть эту ошибку обработает, то надо использовать исключения.

То что твой user.TryDoOperation ориентирован на конкретного клиента, мешает использовать его в другом клиенте.

S>>В случае с Dictionary.TryGetValue — я с тобой соглашусь, потому как Dictionary отлажен и получить что либо кроме ArgumentNullException практически нереально. При внутри вызова метода не происходит обращений к внешним источникам данных и сервисам, которое может окончится неудачей.


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

T>Но в этом случае поиск по словарю произойдет дважды. TryGetValue позволяет обойтись одним поиском то есть этот метод используется для оптимизации.

А что оптимизирует TryDoOperation?

S>>В случае с user.TryDoOperation — нет. Это во-первых сфероконь, реализация которого может быть неверна, он скорее всего обращается к внешним сервисам и источникам данных, наличие и состояние которых может быть под вопросом (если речь идет не о крестиках-ноликах).


T>Ну да, и что?


S>>Твой Usage заставляет меня сделать два взаимоисключающих предположения:

S>>1) TryDoOperation катчит все исключения и пакует их в OperationResult.
T>Нет, он точно не перехватывает все исключения. Он перехватывает только те исключения, которые ожидает. Все исключения перехватывать нельзя.

S>>Тогда я не понимаю, как можно сделать пакетную обработку

S>>foreach(user in users) user.TryDoOperation()
S>>судя по всему придется анализировать результат после каждого пользователя и решать, стоит ли продолжать или какой-то сервис не доступен...
S>>2) TryDoOperation катчит не все исключения. Тогда видимо в случае таких исключений пользователь не увидит MessageBox с надписью "Failed".
T>Да, он увидит обобщенное сообщение об ошибки. Это значит, что произошла какая-то непредвиденная ситуация. Например, закончилась память (OutOfMemory), или сгорела сетевая карта, у процесса нехватает прав на обращение к сетевому интерфейсу, либо где-то есть ошибка в коде (например, ArgumentNullException). В этом случае лучшее, что мы можем сделать — это сообщить службе поддержки о проблеме, предоставив ей максимум информации. Следовательно ошибку надо подробно залоггировать, можно послать соответствующий email кому надо, а пользователю показать окно с текстом "Упс, что-то хреновое произошло!"

"Упс" показал, а "Failed" — не показал. А бизнес-исключительные ситуации не нуждаются в отправке email-а и логировании?

S>>А что если все операции над моделью данных возвращают OperationResult? Их же придется комбинировать, а значит в каждой операции анализировать чем окончилась вложенная...

S>>Ты работал плотно с COM из C++, где после каждого чиха нужно проверить HRESULT?
T>Замечательно, ты наверное, придумал случай, где коды возврата подходят плохо.
Ты мне льстишь, не я его придумал.

T>Тогда я цитирую самого себя:

T>

T>Я не говорю что этот способ универсален. Где-то лучше исключения, а где-то коды возврата.

Re[33]: –
От: mrTwister Россия  
Дата: 22.08.10 18:04
Оценка:
Здравствуйте, samius, Вы писали:

S>Меня смущает ветка else в твоем коде. При наличии любого исключения пользователь не увидит сообщение "Failed". А значит может думать что операция прошла, чихать что сработал обработчик верхнего уровня и пополнил лог... То есть при том, что ты назадавал мне правильных вопросов, твой Usage будет работать странновато.

Работать он будет абсолютно правильно. Осмысленно обработать мы можем только те исключения, которые ожидаем. Если же из операции вылетела неведомая хрень, например NullReferenceException, ArgumentException, StackOverflowException, OutOfMemoryException и т.д., то лучшее, что мы можем сделать — это срочно завершить текущую операцию и сообщить что надо куда надо. Хуже если мы будем пытаться строить хорошую мину при плохой игре, выполняя обработчик ошибок в ситуациях, на которые этот обработчик не был рассчитан.

S>То что твой user.TryDoOperation ориентирован на конкретного клиента, мешает использовать его в другом клиенте.

Он ориентирован не столько на конкретного клиента, сколько на конкретный вариант использования. Это нормально. Благодаря чему в этом варианте использования библиотека очень удобна. Когда ты разрабатываешь библиотеку обычно можно вполне определенно предположить, как эту библиотеку будут использовать. Если же библиотека очень обобщенная и варианты использования могут быть какими угодно, то можно оставить и пару методов: один с исключениями, а другой с кодами возврата. Посмотри, например на LINQ — это как раз пример такой обобщенной библиотеки. Дак вот, там есть, например, метод First, который бросает исключения, и метод FirstOrDefault, который делает тоже самое, но исключение не кидает, а возвращает код возврата.

S>А что оптимизирует TryDoOperation?

Количество обращений к серверу и возможность обойтись без бизнес-транзакций.

S>"Упс" показал, а "Failed" — не показал. А бизнес-исключительные ситуации не нуждаются в отправке email-а и логировании?

Если мы их ожидаем, то не нуждаются.
лэт ми спик фром май харт
Re[34]: –
От: samius Япония http://sams-tricks.blogspot.com
Дата: 22.08.10 19:49
Оценка:
Здравствуйте, mrTwister, Вы писали:

T>Здравствуйте, samius, Вы писали:


S>>Меня смущает ветка else в твоем коде. При наличии любого исключения пользователь не увидит сообщение "Failed". А значит может думать что операция прошла, чихать что сработал обработчик верхнего уровня и пополнил лог... То есть при том, что ты назадавал мне правильных вопросов, твой Usage будет работать странновато.

T>Работать он будет абсолютно правильно. Осмысленно обработать мы можем только те исключения, которые ожидаем. Если же из операции вылетела неведомая хрень, например NullReferenceException, ArgumentException, StackOverflowException, OutOfMemoryException и т.д., то лучшее, что мы можем сделать — это срочно завершить текущую операцию и сообщить что надо куда надо. Хуже если мы будем пытаться строить хорошую мину при плохой игре, выполняя обработчик ошибок в ситуациях, на которые этот обработчик не был рассчитан.
В случае ожиданных исключений, юзер увидит сообщение "Failed". В случае неожиданных — не увидит. Вот это я называю странным поведением.

S>>То что твой user.TryDoOperation ориентирован на конкретного клиента, мешает использовать его в другом клиенте.

T>Он ориентирован не столько на конкретного клиента, сколько на конкретный вариант использования. Это нормально. Благодаря чему в этом варианте использования библиотека очень удобна. Когда ты разрабатываешь библиотеку обычно можно вполне определенно предположить, как эту библиотеку будут использовать. Если же библиотека очень обобщенная и варианты использования могут быть какими угодно, то можно оставить и пару методов: один с исключениями, а другой с кодами возврата. Посмотри, например на LINQ — это как раз пример такой обобщенной библиотеки. Дак вот, там есть, например, метод First, который бросает исключения, и метод FirstOrDefault, который делает тоже самое, но исключение не кидает, а возвращает код возврата.
Ты путаешь. Код возврата — это прежде всего число. FirstOrDefault возвращает значение или default(T), которое в общем случае с числом не имеет ничего общего.

S>>А что оптимизирует TryDoOperation?

T>Количество обращений к серверу и возможность обойтись без бизнес-транзакций.
Чем количество обращений к серверу будет меньше чем при DoOperation?

S>>"Упс" показал, а "Failed" — не показал. А бизнес-исключительные ситуации не нуждаются в отправке email-а и логировании?

T>Если мы их ожидаем, то не нуждаются.
Вот скажи, попытка закрытия счета при отрицательном балансе для тебя неожиданная ситуация, или ее не нужно логировать?
Re[35]: –
От: mrTwister Россия  
Дата: 22.08.10 20:14
Оценка:
Здравствуйте, samius, Вы писали:

S>В случае ожиданных исключений, юзер увидит сообщение "Failed". В случае неожиданных — не увидит. Вот это я называю странным поведением.

И чем же оно странно? В Windows тоже пользователь может увидеть и messagebox с сообщением "Access denied", и "программа выполнила недопустимую операцию и будет закрыта" или, даже, синий экран. Как думаешь, зачем майкросовтовцы сделали синий экран, дураки?

S>Ты путаешь. Код возврата — это прежде всего число.

Кто это тебе такую глупость сказал?

S>>>А что оптимизирует TryDoOperation?

T>>Количество обращений к серверу и возможность обойтись без бизнес-транзакций.
S>Чем количество обращений к серверу будет меньше чем при DoOperation?
Нет. Оно будет меньше, чем при tester-doer

S>Вот скажи, попытка закрытия счета при отрицательном балансе для тебя неожиданная ситуация, или ее не нужно логировать?

Это зависит от того, как мы реализовали систему. Если мы её реализовали таким образом, что у нас в принципе не может возникнуть попытка закрытия счета при отрицательном балансе и наличие такой попытки говорит либо об ошибке в программном коде, либо неконсистентности данных, либо о хакерской атаке, то это неожиданная ситуация. Если же эта ситуация может возникнуть в рамках штатного функционирования системы, то это ожидаемая ситуация и логгировать её не надо. Но только не путаем логгирование с аудированием.
лэт ми спик фром май харт
Re: Исключения для пользователя и для программиста -- разниц
От: Pzz Россия https://github.com/alexpevzner
Дата: 22.08.10 21:07
Оценка:
Здравствуйте, 0K, Вы писали:

0K>Почему их (эти 2 категории исключений) никак не отличают? Сделали бы стандарт: все исключения, содержащие информацию полезную для пользователя наследовать от UserFriendlyException. А остальные инсключения -- контрактные -- не должны показываться пользователю.


А что делать с этими самыми UserFriendlyException, если они на английском, а разговор с пользователем строится на китайском?
Re[36]: –
От: samius Япония http://sams-tricks.blogspot.com
Дата: 23.08.10 02:51
Оценка:
Здравствуйте, mrTwister, Вы писали:

T>Здравствуйте, samius, Вы писали:


S>>В случае ожиданных исключений, юзер увидит сообщение "Failed". В случае неожиданных — не увидит. Вот это я называю странным поведением.

T>И чем же оно странно? В Windows тоже пользователь может увидеть и messagebox с сообщением "Access denied", и "программа выполнила недопустимую операцию и будет закрыта" или, даже, синий экран. Как думаешь, зачем майкросовтовцы сделали синий экран, дураки?

Т.е. для тебя норма, когда приложение вместо внятного сообщения о том что операция "Failed" показывает что-то вроде "Object reference not set to an instance of an object.", на который я смотрел 4 года в 2005/2008 студиях?

S>>Ты путаешь. Код возврата — это прежде всего число.

T>Кто это тебе такую глупость сказал?
Про число — это я глупость ляпнул. Но все равно, код возврата и специальное значение это далеко не одно и то же.

S>>>>А что оптимизирует TryDoOperation?

T>>>Количество обращений к серверу и возможность обойтись без бизнес-транзакций.
S>>Чем количество обращений к серверу будет меньше чем при DoOperation?
T>Нет. Оно будет меньше, чем при tester-doer

Если ты не заметил, то я был против tester-doer. Так что ты мне какой-то странный аргумент привел.

S>>Вот скажи, попытка закрытия счета при отрицательном балансе для тебя неожиданная ситуация, или ее не нужно логировать?

T>Это зависит от того, как мы реализовали систему. Если мы её реализовали таким образом, что у нас в принципе не может возникнуть попытка закрытия счета при отрицательном балансе и наличие такой попытки говорит либо об ошибке в программном коде, либо неконсистентности данных, либо о хакерской атаке, то это неожиданная ситуация. Если же эта ситуация может возникнуть в рамках штатного функционирования системы, то это ожидаемая ситуация и логгировать её не надо.
Ты воспринимаешь систему как однажды отлитую в железобетоне, а на самом деле система может развиваться годами, причем разными людьми. Необходимость лоГирования попытки закрытия счета при отрицательном балансе обуславливается ТЗ, а не фантазией программиста.

T>Но только не путаем логгирование с аудированием.

Если я правильно тебя понял, то под лоГГированием ты подразумеваешь запись в лоГГ файл (то есть логирование), а под аудированием — понимание воспринимаемой на слух речи... А кто тут путает эти вещи? К чему реплика?
Re[37]: –
От: mrTwister Россия  
Дата: 23.08.10 04:07
Оценка:
Здравствуйте, samius, Вы писали:

S>Т.е. для тебя норма, когда приложение вместо внятного сообщения о том что операция "Failed" показывает что-то вроде "Object reference not set to an instance of an object.", на который я смотрел 4 года в 2005/2008 студиях?

Нет, с чего ты взял?

S>>>Ты путаешь. Код возврата — это прежде всего число.

T>>Кто это тебе такую глупость сказал?
S>Но все равно, код возврата и специальное значение это далеко не одно и то же.
А в чем разница?

S>Если ты не заметил, то я был против tester-doer. Так что ты мне какой-то странный аргумент привел.

Какой еще аргумент?

S>Ты воспринимаешь систему как однажды отлитую в железобетоне, а на самом деле система может развиваться годами, причем разными людьми. Необходимость лоГирования попытки закрытия счета при отрицательном балансе обуславливается ТЗ, а не фантазией программиста.


В этом случае это называется аудит. Это не тоже самое, что логгирование.
лэт ми спик фром май харт
Re[38]: –
От: samius Япония http://sams-tricks.blogspot.com
Дата: 23.08.10 06:09
Оценка:
Здравствуйте, mrTwister, Вы писали:

T>Здравствуйте, samius, Вы писали:


S>>Т.е. для тебя норма, когда приложение вместо внятного сообщения о том что операция "Failed" показывает что-то вроде "Object reference not set to an instance of an object.", на который я смотрел 4 года в 2005/2008 студиях?

T>Нет, с чего ты взял?
Ты сам написал что не видишь ничего странного в таком поведении.

И чем же оно странно? В Windows тоже пользователь может увидеть...


S>>>>Ты путаешь. Код возврата — это прежде всего число.

T>>>Кто это тебе такую глупость сказал?
S>>Но все равно, код возврата и специальное значение это далеко не одно и то же.
T>А в чем разница?
В том что в код возврата не принято паковать возвращаемое значение функции. При наличии кода возврата возвращаемое значение передается через выходные параметры. В код возварта пакуют код ошибки и (внимание!) специальное значение кода ошибки — признак успеха.
В свою очередь специальное значение используют для информирования о неудачах выполнения функции (типа значение не найдено), но не об ошибках выполнения функции или о несоблюдении инварианта.
Примеры:
Array.IndexOf — возвращает индекс и в качестве специального значения (-1) признак отсутствия искомого элемента.
Enumerable.FirstOrDefault — возвращает T и в качестве специального значения default(T) признак отсутствия элемента. То что это спорное решение для значимых типов неоднократно обсуждалось.
Dictionary.TryGetValue — возвращает признак наличия записи.
WinAPI FindWindow возвращает HWND и в качестве специального значения (NULL) признак неудачи, при этом за кодом ошибки (возврата) следует обращаться к GetLastError, которая вернет код возврата.

IUnknown::QueryInterface — возвращает код возврата, и искомый интерфейс через out параметр.

Для комбинирования кода возврата и возвращаемого значения следует использовать высокоуровневые конструкции типа кортежей, АТД, или записей. Однако, я не могу запретить использовать одно значение примитивного типа для кода возврата и возвращаемого значения одновременно.


S>>Если ты не заметил, то я был против tester-doer. Так что ты мне какой-то странный аргумент привел.

T>Какой еще аргумент?
Что TryDoOperation минимизирует число обращений к серверу

S>>Ты воспринимаешь систему как однажды отлитую в железобетоне, а на самом деле система может развиваться годами, причем разными людьми. Необходимость лоГирования попытки закрытия счета при отрицательном балансе обуславливается ТЗ, а не фантазией программиста.


T>В этом случае это называется аудит. Это не тоже самое, что логгирование.

То есть ты под аудированием подразумевал аудитирование?
Re[39]: –
От: mrTwister Россия  
Дата: 23.08.10 06:47
Оценка:
Здравствуйте, samius, Вы писали:

S>Здравствуйте, mrTwister, Вы писали:


T>>Здравствуйте, samius, Вы писали:


S>>>Т.е. для тебя норма, когда приложение вместо внятного сообщения о том что операция "Failed" показывает что-то вроде "Object reference not set to an instance of an object.", на который я смотрел 4 года в 2005/2008 студиях?

T>>Нет, с чего ты взял?
S>Ты сам написал что не видишь ничего странного в таком поведении.
S>

S>И чем же оно странно? В Windows тоже пользователь может увидеть...

Смотри, у нас есть две ситуации:

Ситуация А.
Пользователь пробует выполнить операцию, а ему сообщают, что операция не может быть выполнена, так как у пользователя недостаточно средств на счету.

Ситуация Б.
Пользователь пробует выполнить операцию и тут наступает песец. Умирает винчестер, а в дата-центр попала ядерная бомба. В этом случае пользователь видит сообщение:
"Произошел непредвиденный сбой в системе. Приносим свои извинения. Пожалуйста, обратитесь в саппорт. Телефон саппорта: 123-123-123"

Это две совершенно разные ситуации, которые надо обрабатывать совершенно по-разному.

S>В том что в код возврата не принято паковать возвращаемое значение функции...

Ок, предлагаю не углубляться в терминологический сбор, это бессмысленно. Я под кодом возврата имел ввиду возврат статуса выполнения функции через прямой поток выполнения (то есть через возвращаемое значение, или out параметры). Исключения — это альтернативный поток выполнения. Дак вот, правило, о котором я толкую, можно переформлировать так: проектировать библиотеку надо стараться таким образом, чтобы клиенту не надо было перенаправлять один поток выполнения в другой (альтернативный поток в прямой и прямой поток в альтернативный). Таких точек перенаправления должно быть минимум.

S>>>Если ты не заметил, то я был против tester-doer. Так что ты мне какой-то странный аргумент привел.

T>>Какой еще аргумент?
S>Что TryDoOperation минимизирует число обращений к серверу
Минимизирует в сравнении с tester-doer, а не в сравнении с try/catch. Если касаться кода со словарем, то код
if(!dictionary.ContainsKey(key))
{
    dictionary[key] = value;
}
return dictionary[key];


можно было бы переписать не только через TryGetValue, но и вот так:

try
{
    return dictionary[key];
}
catch(KeyNotFoundException)
{
    dictionary.Add(key, value);
    return value;
}

Причем, если не учитывать оверхеда на сами исключения, то этот код алгоритмически эквивалентен коду с TryGetValue, что не помешало Майкросовту добавить в класс словаря метод TryGetValue.

S>То есть ты под аудированием подразумевал аудитирование?

Да
лэт ми спик фром май харт
Re[40]: –
От: samius Япония http://sams-tricks.blogspot.com
Дата: 23.08.10 08:03
Оценка:
Здравствуйте, mrTwister, Вы писали:

T>Смотри, у нас есть две ситуации:


T>Ситуация А.

T>Пользователь пробует выполнить операцию, а ему сообщают, что операция не может быть выполнена, так как у пользователя недостаточно средств на счету.

T>Ситуация Б.

T>Пользователь пробует выполнить операцию и тут наступает песец. Умирает винчестер, а в дата-центр попала ядерная бомба. В этом случае пользователь видит сообщение:
T>"Произошел непредвиденный сбой в системе. Приносим свои извинения. Пожалуйста, обратитесь в саппорт. Телефон саппорта: 123-123-123"

T>Это две совершенно разные ситуации, которые надо обрабатывать совершенно по-разному.


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

S>>В том что в код возврата не принято паковать возвращаемое значение функции...

T>Ок, предлагаю не углубляться в терминологический сбор, это бессмысленно. Я под кодом возврата имел ввиду возврат статуса выполнения функции через прямой поток выполнения (то есть через возвращаемое значение, или out параметры). Исключения — это альтернативный поток выполнения. Дак вот, правило, о котором я толкую, можно переформлировать так: проектировать библиотеку надо стараться таким образом, чтобы клиенту не надо было перенаправлять один поток выполнения в другой (альтернативный поток в прямой и прямой поток в альтернативный). Таких точек перенаправления должно быть минимум.

Можно ссылку на это правило?

S>>Что TryDoOperation минимизирует число обращений к серверу

T>Минимизирует в сравнении с tester-doer, а не в сравнении с try/catch. Если касаться кода со словарем, то код
T>можно было бы переписать не только через TryGetValue, но и вот так:

T>
T>try
T>{
T>    return dictionary[key];
T>}
T>catch(KeyNotFoundException)
T>{
T>    dictionary.Add(key, value);
T>    return value;
T>}
T>

T>Причем, если не учитывать оверхеда на сами исключения, то этот код алгоритмически эквивалентен коду с TryGetValue, что не помешало Майкросовту добавить в класс словаря метод TryGetValue.
Во избежание оверхеда на исключения и для упрощения кода для доступа к содержимому.
Какие проблемы решает TryDoOperation vs DoOperation, кроме оверхеда на исключения?

S>>То есть ты под аудированием подразумевал аудитирование?

T>Да
Тогда отвечу что аудитирование и логирование может выполняться одним аппаратом типа ExceptionPolicy, при этом нет смысла различать на уровне кода бизнес-исключения от других типа ресолва DNS. Это может быть сделано на уровне конфигурации программы.
Re[11]: Резюме
От: Qbit86 Кипр
Дата: 23.08.10 11:51
Оценка: 17 (2)
Здравствуйте, 0K.

http://bik-top.livejournal.com/49233.html
Глаза у меня добрые, но рубашка — смирительная!
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.