Здравствуйте, Mike Chaliy, Вы писали:
MC>Интерсно мнение, может ли идеологически сеттер кидать исключение?
MC>Мое ИМХО сеттер не должен кидать никаких исключений. Валидация данных должна проиходить на момент дейтсвия.
А по чему Setter не может кидать исключения??
Вы представте вы что у вас просто метод
void SetName(string name)
При этом не надо забывать что у инстансов есть свое собственное состояние, на основании которого они могут принимать те или иные решения.
И вообще как же тогда жить с Lazy Load?
Здравствуйте, C...R...a...S...H, Вы писали:
CRA>А по чему Setter не может кидать исключения?? CRA>Вы представте вы что у вас просто метод
CRA>
CRA>void SetName(string name)
CRA>
CRA>При этом не надо забывать что у инстансов есть свое собственное состояние, на основании которого они могут принимать те или иные решения. CRA>И вообще как же тогда жить с Lazy Load?
Подчеркну я специально говорю про идеолгию. Как бы вы предпочли реализовать сеттер для Age в моем примере. Проверив его на больше чем 0 и кинув ексепшен? Или дали бы туда попасть -10, а потом уже на момент действия проверили что человек не может пить если ему меньше чем 0 лет?
По ходу я обычно не представляю методы . Я делаю методы. Так если у меня состояние обьекта может сломаться если я не проконтролирую что туда присваеваеться, то я делаю специальный метод SetName. И там уже кидаю ексепшен. Но это все мое ИМХО. Мне интерсно ваше .
MC>Подчеркну я специально говорю про идеолгию. Как бы вы предпочли реализовать сеттер для Age в моем примере. Проверив его на больше чем 0 и кинув ексепшен? Или дали бы туда попасть -10, а потом уже на момент действия проверили что человек не может пить если ему меньше чем 0 лет?
MC>По ходу я обычно не представляю методы . Я делаю методы. Так если у меня состояние обьекта может сломаться если я не проконтролирую что туда присваеваеться, то я делаю специальный метод SetName. И там уже кидаю ексепшен. Но это все мое ИМХО. Мне интерсно ваше .
Мое мнение такое, что exception в setter'ах это нормально.
Другой вопрос как далеко можно зайти в проверках, но стандартные проверки может ли данное значение быть установлено, думаю стоит размещать в сеттерах.
Я повторюсь: не всегда внутреннее состояние объекта может быть доступно из вне.
Как пример: pattern State
Когда вам приходит объект в каком то состоянии и вы пытаетесь его использовать не правильным образом
Здравствуйте, C...R...a...S...H, Вы писали:
CRA>Мое мнение такое, что exception в setter'ах это нормально.
ОК, пасиба за ответы, можно я еще по задаю вопросов?
А вы пишите ожидаемые исключения когда пишите дведокс для свойств?
Пример.
/// <summary>
/// Get or set age.
/// </summary>
/// <exception cref="ArgumentOutOfRangeException">Throws when age is less then zero.</exception>
/// <returns></returns>
Вы как пользователь АПИ в каких случаях ожидаете что Person для свойства Age крешанеться? Как обычно выглядит код который вы пишете?
try
{
Person p = new Person();
p.Age = -1;
p.Age = 10;
p.Drink();
}
catch(ArgumentException e)
{
// Bit strange...
}
или
try
{
Person p = new Person { Age = 0 };
}
catch(ArgumentException e)
{
// Bit strange...
}
Так же как для таких методов System.Threading.Thread.IsBackground
Exceptions:
System.Threading.ThreadStateException: The thread is dead.
System.Configuration.ConfigurationElement
Exceptions:
System.Configuration.ConfigurationErrorsException: The element has already been locked at a higher configuration level.
System.Net.HttpWebRequest.AutomaticDecompression
Exceptions:
System.InvalidOperationException: The object's current state does not allow this property to be set.
Здравствуйте, Mike Chaliy, Вы писали:
MC>Здравствуйте, C...R...a...S...H, Вы писали:
CRA>>Последний пример я думаю очень показательный.
MC>Жаль что вы не ответили на вопросы.... Если вам охота примеров от Майкрософт, то зайдите в любой контрол. Весь дизайнер на этом построен.
Думаю ответ был дан:
The object's current state does not allow this property to be set.
То есть при не правильном использовании объекта, он может матюкаться экшепшанапи даже при установке свойств.
Здравствуйте, Mike Chaliy, Вы писали:
R>[skipped]
Вы, прошу прощения, с какой планеты? Во-первых, на планете Земля человек может спокойно напиться в 13 лет, буквально неделю назад у друга с дочерью такая беда приключилась. Во-вторых, с чего вы взяли, что у человека обязательно должен быть метод Drink? Серьезно, есть трезвенники. В-третьих, кто встроил в человека механизм проверки возраста? Пил в подворотне в 15 лет и, знаете, внутри никакие шестеренки не начинали скрипеть. В-четвертых, согласно КоАП крепкие алкогольные официально позволительны только с 21 года, 18 — это ваш ценз, вы с 18 начали?
Это лирика, но очень показательная. Во-первых, проверка, которую вы засунули в метод Drink — не является ответственностью Person. Во-вторых, если ваш Person — это некоторый вид людей, которые пить начинают строго на следующий день после 18-летия, а также могут иметь отрицательный возраст, то все OK, но в отношении людей планеты Земля корректнее устанавливать значение age в конструкторе, а при несоблюдении переданным в конструктор значением age контракта ОДЗ (математику помните?) возраста человека — кидать исключение. Было бы неплохо сразу при попытке создать человека с возрастом -5 лет (ага, родится через 5 лет только?), получить исключение, причем осмысленное "это не человек!", а не в момент попытки напоить это нечто с очень неосмысленным исключением "ему нет 18!" (ему не просто нет 18, оно не человек). В-третьих, кошернее использовать стандартные исключения, если логически таковые существуют в SDK, в конструкторе, к примеру, ArgumentException.
Исключения должны кидать конструкторы тем самым отменяя создание, полностью исключая появления в памяти объекта с недопустимыми характеристиками. Аналогично, для mutable-объектов подобные же проверки должны быть в setter. Могу напугать: есть движки защитного программирования, которые могут кинуть исключение из getter-а.
Здравствуйте, C...R...a...S...H, Вы писали:
CRA>Думаю ответ был дан:
CRA>
CRA>The object's current state does not allow this property to be set.
CRA>То есть при не правильном использовании объекта, он может матюкаться экшепшанапи даже при установке свойств.
Тоесть если я вам приведу пример где проверка и исключение делаеться на вызов, то вы тоже безаговорочно примите? Ну или давайте по другому. Вы уверены что все что сделано в фремворке идеологически правильно? Вы уверены что это небыло исключением из идеологии?
MC>Тоесть если я вам приведу пример где проверка и исключение делаеться на вызов, то вы тоже безаговорочно примите? Ну или давайте по другому. Вы уверены что все что сделано в фремворке идеологически правильно? Вы уверены что это небыло исключением из идеологии?
Вот вы задумайтесь.
У вас есть объект состояниме которого вы не управляете.
Тоже самое соединение с другим серверм.
Вы его открывает и с ним работаете.
Через некоторое время соединение отрубается, но вы об этом пока не знаете, а просто пытаетесь установить размер пакета для пересылки данных.
Как должен вести себя объект в таком случае?
Здравствуйте, Mike Chaliy, Вы писали:
MC>Интерсно мнение, может ли идеологически сеттер кидать исключение?
MC>Мое ИМХО сеттер не должен кидать никаких исключений. Валидация данных должна проиходить на момент дейтсвия.
Нужно!!!
1)Объект должен поддерживать только корректное состояние, а не быть своего рода свалкой.
2)При твоём подходе — get должен относиться к моменту действия.
Если такового не будет, то вообще лажа, тобишь, появляется вероятность того, что что сущности которые связаны с твоим Person могут оперировать не допустимыми данными.
3)На момент опрашивания состояния объекта(дёрнуть get) — не желательно кидать исключение и производить какие то действия.
По крайней мере разработчик, который будет использовать твой Person мягко говоря удивиться — такому поведению
4)Как дебажить это дело собираешься? Задал возраст равным -1 и пошёл процесс, после этого ты опросил Person и получил исключение
и хочется же узнать, откуда взялся -1 а в StackTrace уже и в помине нет места, в котором задавался возраст равным -1.
5)Если провалидируешь возраст на сеттере то ты это сделаешь в одном месте!!!!!!!
А если на момент совершения действия, то успевай только отслеживать в каких местах используется возраст и расставлять перед вызовами метод аля ValidateAge(). А это уже COPY+PASTE.
6) Модульное тестирование при этом усложняется.
А какие же плюсы во всём этом, дай подумать ...Хм ... Что то не видать!
Здравствуйте, kisel, Вы писали:
K>1)Объект должен поддерживать только корректное состояние, а не быть своего рода свалкой.
Если я хочу потдерживать корректное состояние то у меня по умолчанию все свойства будут ридонли. У меня будут методы которые будут это состояние менять. Так например у меня есть аппонтмент.
class Appointemet
{
DateTime startTime;
DateTime endTime;
int Duration; // у меня по бизнесу это дени.
}
Если мне надо аппоинтменту укзать его даты то будет метод SetDates(DateTime start, DateTime end) этот же метод завалидирует что end > start, посчитает дюрейшен.
Если обратиться к примеру с Age то вобщемто это такой же пример. Если мне необходимое корректное состояние и если у меня по бизнесу не может быть Age меньше нуля, то при вызове SetAge я крешанусь. Все ок, я же ожидаю что мое действие может привести к исключениям.
Єто реально мое ИМХО что юзабилити АПИ значительно выше, если сеттеры не кидают исключения. Точно так же как мое ИМХО что сеттеры не должны изменять соседние поля....
K>4)Как дебажить это дело собираешься? Задал возраст равным -1 и пошёл процесс, после этого ты опросил Person и получил исключение
Обьясню. Возраст -1 это вполне нормальное значение. Тоесть существуют таки задачи в которых это необходимо. Точно также как и со складами. Какбудтобы физически не может быть на складе -20 мочалок. Ан нет, может, это более чем повседневная задача в складском учете.
Дальше больше предположим что -1 это не валидное состояние по бизнесу.
Тогда есть два варианта.
а) Наша реализация это потдерживает! Тоесть у обьекта появляеться два состояния валидное, которое предполагаеться что юзер исправит до сохранения и валидное, кторое мы можем спокойно сохранить. Паралели с реальным миром тут вообще неприемлемы, вон в медециноском програмном обеспечении часто персоны до какогото возраста (14 лет) вообще не персоны...
б) Не потдерживает. Тоесть просто крешиться на момент установки.
В любом случае момент установки это действие.
K>5)Если провалидируешь возраст на сеттере то ты это сделаешь в одном месте!!!!!!!
Основная мысля это то что для каждого БЛ метода это может быть свое. Тоесть для Дринк должно быть 18 (21, хз у меня не было случая проверить...), а для метода Слип вообще не существенно какой возраст....
K>Итого ... выкинь эту идею из головы
Я же не советов просил. Тем более меня слегка удивляет пренебрежительное повествование вас и rsn81, но мы люди не гордые... Инфа полезней, так что проглотим.
Здравствуйте, kisel, Вы писали:
>>>Здравствуйте, Mike Chaliy, Вы писали:
K>5 аргумент не считается, забыл что у нас же предполагается валидировать на get-е
Не-не, я нигде не прдлагал валидировать в геттере. Геттеры по гаидлайнам не должны кидать ексепшены, как и констуркторы собсно. Так что ваше замечание вполне валидно и я на него даже ответил .
Здравствуйте, Mike Chaliy, Вы писали: MC>Я же не советов просил. Тем более меня слегка удивляет пренебрежительное повествование вас и rsn81, но мы люди не гордые... Инфа полезней, так что проглотим.
Так мы любя ... Хорошо, что не гордый ...
По поводу того, что должно быть свойство Age и метод SetAge — как то сложновато закрутил.Код должен быть — friendly.
Если бы мне пришлось юзать этот классец, то первая мысль пришла бы в голову, зачему 2 метода нужны (свойство — это всего лишь 2 сгруппированных метода, может быть и один).
У меня бы мозг закипел и подумал про себя — "снова какой-то индусский код подсунули "
Здравствуйте, Mike Chaliy, Вы писали:
MC>Не-не, я нигде не прдлагал валидировать в геттере. Геттеры по гаидлайнам не должны кидать ексепшены, как и констуркторы собсно. Так что ваше замечание вполне валидно и я на него даже ответил .
Выделенное — что за гайдлайны такие?
Здравствуйте, rsn81, Вы писали:
R>Здравствуйте, Mike Chaliy, Вы писали:
MC>>Не-не, я нигде не прдлагал валидировать в геттере. Геттеры по гаидлайнам не должны кидать ексепшены, как и констуркторы собсно. Так что ваше замечание вполне валидно и я на него даже ответил . R>Выделенное — что за гайдлайны такие?
Хотя я там слишком жестко написал, в гаидлайнах речь идет про "хотелось бы что бы не кидали". "не должны" лучше читать как "не хотлоесь бы". Но так как весь суть разговора идет про идеолгическую часть, которая по необходисомти может быть попрана. То ИМХО вполне допустимо использовать черное и белое.
Здравствуйте, Mike Chaliy, Вы писали:
MC>Здравствуйте, kisel, Вы писали:
K>>У меня бы мозг закипел и подумал про себя — "снова какой-то индусский код подсунули "
MC>А у меня четкие ассоциации с индусами, когда вижу код свойств с логикой...
Зачем в таком случаи тебе вообще свойства, псевдо- инкапсуляцию реализуешь, тобишь, "простые" свойства делаешь?
Всё таки свойство с set-ом Age и метод SetAge(..) — плохой дизайн, если не сказать более.
На уровне IL (Intermediate Language) — нет понятия свойств, это так замануха C#, для удобства сделано.
Откуда вообще такое веяние пошло? Сам придумал или где вычитал? (имею ввиду, одновременно использовать свойство Age{...} и метод SetAge(..))
Здравствуйте, Mike Chaliy, Вы писали:
MC>.Net Framework Design Guidelines MC>тут есть видио http://channel9.msdn.com/pdc2008/PC58/, помойму на 20 минуте про геттеры, чуть дальше про контсрукторы.
А текстом нет?
MC>Хотя я там слишком жестко написал, в гаидлайнах речь идет про "хотелось бы что бы не кидали". "не должны" лучше читать как "не хотлоесь бы". Но так как весь суть разговора идет про идеолгическую часть, которая по необходисомти может быть попрана. То ИМХО вполне допустимо использовать черное и белое.
Не, очень даже хотелось бы, чтобы любая попытка создать объект с некорректным состоянием завершалась исключением, так как исключение в конструкторе не только информирует клиентскую сторону о некорректном действии, но и отменяет создание такого объекта. Советую посмотреть исходники более опытных коллег, чтобы убедиться в том, что конструкторы очень даже злоупотребляют исключениями. Советую почитать статьи о защитном программировании, искать по словам: защитное, безопасное, контрактное, dbc, design by contract.
Здравствуйте, Mike Chaliy, Вы писали:
MC>Если я хочу потдерживать корректное состояние то у меня по умолчанию все свойства будут ридонли. У меня будут методы которые будут это состояние менять. Так например у меня есть аппонтмент. MC>class Appointemet MC>{ MC> DateTime startTime; MC> DateTime endTime; MC> int Duration; // у меня по бизнесу это дени. MC>} MC>Если мне надо аппоинтменту укзать его даты то будет метод SetDates(DateTime start, DateTime end) этот же метод завалидирует что end > start, посчитает дюрейшен.
Во-первых, вполне допустим вариант, когда доступны set-секции свойств StartTime и EndTime (а указанного вами метода или нет вовсе, или он является дополнительным и использует внутри себя эти свойства), каждая из которых производит соответствующую проверку. Во-вторых, длительность надо считать лениво, то есть по запросу, к примеру, в секции get свойства Duration, а сразу в момент установки даты (дат) это вовсе не нужно. Итого, ваш пример в отношении setter-ов не показателен.
MC>Если обратиться к примеру с Age то вобщемто это такой же пример. Если мне необходимое корректное состояние и если у меня по бизнесу не может быть Age меньше нуля, то при вызове SetAge я крешанусь. Все ок, я же ожидаю что мое действие может привести к исключениям. MC>Єто реально мое ИМХО что юзабилити АПИ значительно выше, если сеттеры не кидают исключения. Точно так же как мое ИМХО что сеттеры не должны изменять соседние поля....
Хороший пацанский аргумент, что-то посерьезнее будет?
MC>Обьясню. Возраст -1 это вполне нормальное значение. Тоесть существуют таки задачи в которых это необходимо. Точно также как и со складами. Какбудтобы физически не может быть на складе -20 мочалок. Ан нет, может, это более чем повседневная задача в складском учете. MC>Дальше больше предположим что -1 это не валидное состояние по бизнесу. MC>Тогда есть два варианта. MC>а) Наша реализация это потдерживает! Тоесть у обьекта появляеться два состояния валидное, которое предполагаеться что юзер исправит до сохранения и валидное, кторое мы можем спокойно сохранить. Паралели с реальным миром тут вообще неприемлемы, вон в медециноском програмном обеспечении часто персоны до какогото возраста (14 лет) вообще не персоны... MC>б) Не потдерживает. Тоесть просто крешиться на момент установки. MC>В любом случае момент установки это действие.
Рассмотрим пример бизнес-процесса розничной продажи алкогольной продукции. С точки зрения бизнес-логики допустимым (если вам проще на птичьем языке, то "валидным") покупателем является человек, которому 21 год или старше. Бизнес-логика работает над объектом или его взаимодействием с другими объектами. Но кроме того есть еще и спецификация класса, которая описывает его поведение, эти правила работают только на уровне класса. Спецификация должна описывать область допустимых (область определения в отношении функций в алгебре) входных данных, то есть таких данных, на которых алгоритм преобразования выходных данных в выходные будет соответствовать спецификации. Так вот с точки зрения спецификации класса Человек, человека с возрастом -1 существовать не может (и никакую бизнес-логику тут за уши притягивать не надо). Разница понятна?
MC>Основная мысля это то что для каждого БЛ метода это может быть свое. Тоесть для Дринк должно быть 18 (21, хз у меня не было случая проверить...), а для метода Слип вообще не существенно какой возраст....
Основная ошибка в том, что вы бизнес-логику, которая должна оперировать сущностью, запихиваете внутрь этой сущности. Это также логично, как и в метод Run класса Car засунуть проверку на горение зеленого светофора: эту проверку осуществляет не автомобиль, а человек, который и заставляет машину двигаться — некоторые, кстати, забывают ее осуществить (сам порой пролетаю на красный, при это мое авто глупое нифига мне не сигнализирует!).
MC>Я же не советов просил. Тем более меня слегка удивляет пренебрежительное повествование вас и rsn81, но мы люди не гордые... Инфа полезней, так что проглотим.
А что вы просили?
Здравствуйте, kisel, Вы писали:
K>Всё таки свойство с set-ом Age и метод SetAge(..) — плохой дизайн, если не сказать более.
Погоди я говорил про ридонли Age и метод SetAge... Насчет плохого дизайна, не горячись, неззя вешать ярлыки.
K>Откуда вообще такое веяние пошло? Сам придумал или где вычитал? (имею ввиду, одновременно использовать свойство Age{...} и метод SetAge(..))
, я не знаю откуда такое веяние пошло...
1) Доменные модели. Да Age не может быть меньше нуля, но до момента сохранения это тока ворнинг. При том что классы с потдержкой не валидного состояния тяжелее, они удобней. Я уже приводил пример со складом, там это более наглядно.
2) Тестирование. Не имеет смысл настраивать весь обьект, для того чтобы проверить часть функционала. Все что не нужно для теста — это шум. И например для операции Крикнуть, побарабану какое значение Возраст.
3) Use a property when the member is a logical data member (http://msdn.microsoft.com/en-us/library/bzwdh01d(VS.71).aspx). Ну почему -1 это не данные? Данные. Сегодня для бизнеса они не валидные. Завтра валидные.
4) Свойство это то что изменяет вполне конкретный кусочек состояния (Properties should be stateless with respect to other properties). Сегодня УстановитьВозраст это простая операция которая просто меняет поле Возраст. Завтра эта штука начнет высчитывать возможную дату смерти. И упс, у нас уже это совсем другая семантика. Это я к тому что есть бизнес операция УстановитьВозраст и имхо она точно также операцие должна быть в коде.
Про то что свойства у тебя только get-ы, пропустил .... Что то уже запутался, ты же писал выше: MC>Если обратиться к примеру с Age то вобщемто это такой же пример. Если мне необходимое корректное состояние и если у меня по бизнесу не может быть Age меньше нуля, то при вызове SetAge я крешанусь. Все ок, я же ожидаю что мое действие может привести к исключениям.
Тобишь, на set-ах ты всётаки валидируешь данные.Получается, что говорим об одном и том же.
Собственно вопрос, к чему сводится, использовать set у свойств или методы, что ли?
Здравствуйте, rsn81, Вы писали:
R>Здравствуйте, Mike Chaliy, Вы писали:
MC>>.Net Framework Design Guidelines MC>>тут есть видио http://channel9.msdn.com/pdc2008/PC58/, помойму на 20 минуте про геттеры, чуть дальше про контсрукторы. R>А текстом нет?
До видео, вы можете увидить название книги.
MC>>Хотя я там слишком жестко написал, в гаидлайнах речь идет про "хотелось бы что бы не кидали". "не должны" лучше читать как "не хотлоесь бы". Но так как весь суть разговора идет про идеолгическую часть, которая по необходисомти может быть попрана. То ИМХО вполне допустимо использовать черное и белое. R>Не, очень даже хотелось бы, чтобы любая попытка создать объект с некорректным состоянием завершалась исключением, так как исключение в конструкторе не только информирует клиентскую сторону о некорректном действии, но и отменяет создание такого объекта. Советую посмотреть исходники более опытных коллег, чтобы убедиться в том, что конструкторы очень даже злоупотребляют исключениями. Советую почитать статьи о защитном программировании, искать по словам: защитное, безопасное, контрактное, dbc, design by contract.
Даже не знаю что сьзвить по поводу опытных коллег... Наверное напомнить что в SDK кошерные исключения не находяться.. да и обычные тоже...
Например берем MVC Framework.. упс. Не бросают. Все проверяеться и кидаеться на момент действия. А мож его писали не опытные люди?
Теперь про design by contract, вы заметили слово контракт? Знаете что это обозначает? Ну и как это относиться к бизнес значению Age? Или как это относиться к конструктору который у себя должен сходить в базу данных чтобы перепроверить все ли ок? ИМХО вы пытаетесь задавить бузвордом.
Здравствуйте, rsn81, Вы писали:
R>Здравствуйте, Mike Chaliy, Вы писали:
MC>>Если я хочу потдерживать корректное состояние то у меня по умолчанию все свойства будут ридонли. У меня будут методы которые будут это состояние менять. Так например у меня есть аппонтмент. MC>>class Appointemet MC>>{ MC>> DateTime startTime; MC>> DateTime endTime; MC>> int Duration; // у меня по бизнесу это дени. MC>>} MC>>Если мне надо аппоинтменту укзать его даты то будет метод SetDates(DateTime start, DateTime end) этот же метод завалидирует что end > start, посчитает дюрейшен. R>Во-первых, вполне допустим вариант, когда доступны set-секции свойств StartTime и EndTime (а указанного вами метода или нет вовсе, или он является дополнительным и использует внутри себя эти свойства), каждая из которых производит соответствующую проверку.
Allow properties to be set in any order (http://msdn.microsoft.com/en-us/library/bzwdh01d(VS.71).aspx).
R>Во-вторых, длительность надо считать лениво, то есть по запросу, к примеру, в секции get свойства Duration, а сразу в момент установки даты (дат) это вовсе не нужно. Итого, ваш пример в отношении setter-ов не показателен.
Опять вы учите меня. То что надо будет бизнесу то и будет. В конкретно этом случае у меня подвязан воркфлоу, который работает на изменении дюрейшена.
MC>>Если обратиться к примеру с Age то вобщемто это такой же пример. Если мне необходимое корректное состояние и если у меня по бизнесу не может быть Age меньше нуля, то при вызове SetAge я крешанусь. Все ок, я же ожидаю что мое действие может привести к исключениям. MC>>Єто реально мое ИМХО что юзабилити АПИ значительно выше, если сеттеры не кидают исключения. Точно так же как мое ИМХО что сеттеры не должны изменять соседние поля.... R>Хороший пацанский аргумент, что-то посерьезнее будет?
Судя по всему я не понятно выразился. Меня интерисует лиш ваше мнение. Я не навязываю своего. Я ваше мнение уже понял. Ессно применять не буду.
R>Разница понятна?
Я скипанул, так как к теме это никак не относиться. Если вам хочеться обудсить эту проблему начните новый топик.
MC>>Основная мысля это то что для каждого БЛ метода это может быть свое. Тоесть для Дринк должно быть 18 (21, хз у меня не было случая проверить...), а для метода Слип вообще не существенно какой возраст.... R>Основная ошибка в том, что вы бизнес-логику, которая должна оперировать сущностью, запихиваете внутрь этой сущности.
Я думаю это такая же придирка, что и про АргументЕксепшен. Если не нравиться Дринк, можете заменить его на ПоловоеСозревание...
Вобщемто вы опять не там нашли ошибку. Помойму проблема размещения бизнес логики никак не относиться к теме.
MC>>Я же не советов просил. Тем более меня слегка удивляет пренебрежительное повествование вас и rsn81, но мы люди не гордые... Инфа полезней, так что проглотим. R>А что вы просили?
Вам процитировать? Или вы сами проглядите первый топик?
Здравствуйте, kisel, Вы писали:
K>Здравствуйте, Mike Chaliy, Вы писали:
K>Про то что свойства у тебя только get-ы, пропустил .... Что то уже запутался, ты же писал выше: MC>>Если обратиться к примеру с Age то вобщемто это такой же пример. Если мне необходимое корректное состояние и если у меня по бизнесу не может быть Age меньше нуля, то при вызове SetAge я крешанусь. Все ок, я же ожидаю что мое действие может привести к исключениям.
K>Тобишь, на set-ах ты всётаки валидируешь данные.Получается, что говорим об одном и том же. K>Собственно вопрос, к чему сводится, использовать set у свойств или методы, что ли?
всех запутал... Я не ожидал что настолько все воспримиться в штыки что нанеться валиться куча поучений. Когда валиться куча поучений, приходитья отгавкиваться. Когда отгавкиваешся, получаються курьезы. Еще большие курьезы выходят когда смешиваються ветки.
Про использование метода сет или собсно сеттера я изначально отвечал C...R...a...S...H про обьекты в которых необходимо потдерживать валидное состояние. Мое судя по всему очень субьективное мнение что состояние такого обьекта могут менять тока методы. Даже при том что сеттеры это теже самые методы. Мое опять же субьективное (пацанский, не серьезный аргумент (С) rsn81) что так получаеться значительно более юзабельное/читабельное АПИ.
Mike я хотел бы пару слов добавить ко всем гвоздям, которые мы пытались забить в вашу теорию.
1) чем раньше пользователь получит информацию о том что он делает что-то не правильное, тем лучше для пользователя, думаю вы согласитесь с этой истеной.
2) Следуя вашей теории вообще нет смысла для Свойст. Используйте Field и радуйтесь жизни.
В ответ на все ваши Guidelines Please read C# Programming Guide здесь
Если вам лень читать вот выдержки:
Properties are members that provide a flexible mechanism to read, write, or compute the values of private fields.
Properties enable a class to expose a public way of getting and setting values, while hiding implementation or verification code.
Здравствуйте, C...R...a...S...H, Вы писали:
CRA>Боюсь после этого, можно прекращать обсуждение, если все еще остаетесь при своем мнении, то смысла вас переубеждать нет.
После этого, после чего? Я знаю все эти гаидланы. Я не вижу как это все может влиять на мое мнение. Ну есть у Майкров гаидланы по использованию ДатаСетов, но не юзать же их изза этого? Таких примеров десятки.
Конешно мне было бы проще если у когото оказались бы похожие переживания по поводу оверюза сеттеров... Но судя по всему не оказалось. Ниче, никто изза этого не умрет. Поэтому действительно можно прекращать это обсуждение.
Здравствуйте, Mike Chaliy, Вы писали:
MC>Интерсно мнение, может ли идеологически сеттер кидать исключение?
Если говорить _идеологически_, то нет, исключение в сеттере — это некошерно. По той причине, что сеттер имитирует доступное поле, и какой-то дополнительной логики при установке значения не ожидается. Вы же не ожидаете, например, что здесь
SomeClass i = new SomeClass();
..
SomeClass j = i; (*)
во втором присваивании (*) будет исключение? Если нужно бросить эксепшн — это означает логику, т.е. действие — лучше воспользоваться методом, он в этом плане нагляден.
А вообще вопрос — сродни "использовать ли триггеры в БД" Очень похоже — так же может быть удобно, и так же неочевидно.
Здравствуйте, Mike Chaliy, Вы писали:
MC>Интерсно мнение, может ли идеологически сеттер кидать исключение?
MC>Мое ИМХО сеттер не должен кидать никаких исключений. Валидация данных должна проиходить на момент дейтсвия.
Сеттер сеттеру рознь.
В сеттере domain object я бы не стал. А в сеттере какого нибудь билдера вполне.
Потому как бог знает в каком контексте понадобится использовать доменный обект а с билдером обычно такого не происходит. Вопрос контракта на самом деле.
Здравствуйте, http://code-inside-out.blogspot.com/, Вы писали:
HCI>Здравствуйте, Mike Chaliy, Вы писали:
MC>>Интерсно мнение, может ли идеологически сеттер кидать исключение?
HCI>Если говорить _идеологически_, то нет, исключение в сеттере — это некошерно. По той причине, что сеттер имитирует доступное поле, и какой-то дополнительной логики при установке значения не ожидается.
Объясните зачем тогда вообще вводили свойства, если можно было обойтись полями?
Там было написано русским по белому...
Re[3]: Exceptions в сеттерах
От:
Аноним
Дата:
19.11.08 06:45
Оценка:
Здравствуйте, C...R...a...S...H, Вы писали:
CRA>Объясните зачем тогда вообще вводили свойства, если можно было обойтись полями?
Ничего себе. "Объясните". Вас на Гугле не забаннили?
Свойства ввели для того, чтобы предоставить ограниченный доступ к полям. Например, только на get и т.д.
По поводу исключений — единственный Exception, который стоит использовать внутри свойств, это ArgumentNullException. Но это — exception уровня проектирования; это значит, что-то запрограммировано не так, и он не несет смысла из предметной области; так что его можно заменить, например, на Debug.Assert() (ну если вы совсем пурист)
Здравствуйте, http://code-inside-out.blogspot.com/, Вы писали:
HCI>Ничего себе. "Объясните". Вас на Гугле не забаннили?
Мне интересно было именно ваше видение.
HCI>Свойства ввели для того, чтобы предоставить ограниченный доступ к полям. Например, только на get и т.д.
Так для этого можно было бы методы использовать.
Или вообще могли придумать какой-нить аттрибут для полей аля PublicReadOnly. А тут наворотили:
public int Count
{
get{ return count;}
}
А под конец, для самых простых случаев придумали автоматические свойства.
И очень интересно как вы относитесь к такому вот свойству:
public string Text
{
get
{
string str = (string) this.ViewState["Text"];
if (str != null)
{
return str;
}
return string.Empty;
}
set
{
this.ViewState["Text"] = value;
}
}
Скажу по секрету в StateBag есть такой оператор
throw new ArgumentException();
HCI>По поводу исключений — единственный Exception, который стоит использовать внутри свойств, это ArgumentNullException. Но это — exception уровня проектирования; это значит, что-то запрограммировано не так, и он не несет смысла из предметной области; так что его можно заменить, например, на Debug.Assert() (ну если вы совсем пурист)
В предыдущем вашем посте вы говорили, что выбрасывать исключения — некошерно
Теперь пишете, что мол ArgumentNullException можно, тогда наверное и ArgumentException (как я показал выше) тоже можно
Наверное если продолжить, то так все исключения можно перечислить
Там было написано русским по белому...
Re[5]: Exceptions в сеттерах
От:
Аноним
Дата:
19.11.08 12:11
Оценка:
Здравствуйте, C...R...a...S...H, Вы писали:
CRA>Теперь пишете, что мол ArgumentNullException можно, тогда наверное и ArgumentException (как я показал выше) тоже можно CRA>Наверное если продолжить, то так все исключения можно перечислить
Согласен, тут Вы безусловно правы — то, что я не упомянул про ArgumentException, есть полностью моё упущение.
Однако взгляните выше — туда, где я сказал про исключения, несущие и не несущие смысл предметной области. Я имею в виду, что особые пуристы могут заменить проверку "if(badArg) throw new Argument*Exception()" на Debug.Assert(!badArg), потому как эти исключения не свидетельствуют не о неожиданной ошибке в процессе работы, а о том, что программист съел калошу, когда "кодил"; и все это должно быть устранено ещё на этапе отладки ПО.
>> Автоматические свойства
В них вообще не вижу пользы (Ну кроме, может быть, одной — если впоследствии я решу заменить его на рукописное, клиенты не пострадают. Но такого на моей памяти ещё не было).
>> Интересно ваше видение
Ну вот, я и высказался его, передаю микрофон Вам
Я скажу по-простому. Если не проверять корректность данных, то можно потерять инвариант объекта. А если корректность проверять, то в странной парадигме сеттера ничего не остается, как бросить исключение, так как возвращаемого результата у него в отличии от нормаьного метода нет.
Да здравствует мыло душистое и веревка пушистая.
Re: Проверки в сеттерах приводят к дублированию кода
Здравствуйте, Mike Chaliy, Вы писали:
MC>Мое ИМХО сеттер не должен кидать никаких исключений. Валидация данных должна проиходить на момент дейтсвия.
Пока для проверки достаточно самого присваиваемого значения вроде бы все нормально:
double Age
{
set
{
if (value < 18)
throw new InvalidOperation();
}
}
Однако завтра оказывается, что в разных странах разрешается начинать потреблять спиртные напитки в разном возрасте, соответственно добавляем:
Country Country { get; set; }
double Age
{
set
{
if (value < Country.MinDrinkAge)
throw new InvalidOperation();
}
}
После чего понимаем, что этот код валидность состояния объекта нам не гарантирует, надо так:
Country Country
{
set
{
if (Age < value.MinDrinkAge)
throw new InvalidOperation();
}
}
double Age
{
set
{
if (value < Country.MinDrinkAge)
throw new InvalidOperation();
}
}
Соответственно получаем, что и в сеттере страны нужно проверять и страну, и возраст, и в сеттере возраста нужно проверять и возраст, и страну. Т.е. получили дублирование кода со всеми сопутствующими прелестями.
Есть и еще одна проблема, при проверках в сеттерах при более-менее сложной логике может быть совершенно непонятно в каком порядке надо изменять поля объекта, чтобы не вылетело исключение, т.е. при одном состоянии изменяемого объекта такой код работать будет:
person.Country = Country.Russia;
person.Age = 19;
а такой нет:
person.Age = 19;
person.Country = Country.Russia;
А при другом состоянии изменяемого объекта может быть наоборот, второй вариант будет работать, а первый нет.
Поэтому проверок в сеттерах быть не должно, проверки корректности состояния объекта должны производиться либо по месту действия, либо специальным методом вроде IsValidObject().
Хотя правильнее всего сеттеры вообще не писать, чем меньше в коде сеттеров, тем проще он читается.
Re[2]: Проверки в сеттерах приводят к дублированию кода
Здравствуйте, Undying, Вы писали:
U>Соответственно получаем, что и в сеттере страны нужно проверять и страну, и возраст, и в сеттере возраста нужно проверять и возраст, и страну. Т.е. получили дублирование кода со всеми сопутствующими прелестями.
U>Есть и еще одна проблема, при проверках в сеттерах при более-менее сложной логике может быть совершенно непонятно в каком порядке надо изменять поля объекта, чтобы не вылетело исключение, т.е. при одном состоянии изменяемого объекта такой код работать будет:
U>А при другом состоянии изменяемого объекта может быть наоборот, второй вариант будет работать, а первый нет.
Это, например, неплохо решено в DependencyObject…
U>Поэтому проверок в сеттерах быть не должно, проверки корректности состояния объекта должны производиться либо по месту действия, либо специальным методом вроде IsValidObject().
…и решение данной проблемы оказалось ортогональным валидации. То есть, получается, каждому уровню валидации — своё место и своё поведение, свой результат. И на частном примере "вот так вот плохо", вывод что "вообще нельзя" сомнителен.
U>Хотя правильнее всего сеттеры вообще не писать, чем меньше в коде сеттеров, тем проще он читается.
Уже не раз обсуждалось, что immutable-модель плохо совместима с дизайнерами (за все дизайнеры на свете не поручусь, не знаю, но за дотнетовские — да) компонентов.
Даже структуры (вопреки рекомендациям), даже с перегруженными Equals\GetHashCode (вопреки здравому смыслу) разработчики FCL, , сделали изменяемыми (System.Drawing.Size и System.Windows.Size).
... << RSDN@Home 1.2.0 alpha 4 rev. 1111>>
Help will always be given at Hogwarts to those who ask for it.
Здравствуйте, Vamp, Вы писали:
V>Я скажу по-простому. Если не проверять корректность данных, то можно потерять инвариант объекта. А если корректность проверять, то в странной парадигме сеттера ничего не остается, как бросить исключение, так как возвращаемого результата у него в отличии от нормаьного метода нет.
Простота она иногда хуже воровства с другой стороны KISS тоже не спроста
Проблема как раз в том можем ли мы сформулировать этот самый инвариант так, чтоб покрыть все возможные сценарии использования объекта и при этом с уважением отнестись к SRP...
GIV>Проблема как раз в том можем ли мы сформулировать этот самый инвариант так, чтоб покрыть все возможные сценарии использования
объекта и при этом с уважением отнестись к SRP...
Не можем. И не должны. Бизнес-логика определяет.
Здравствуйте, http://code-inside-out.blogspot.com/, Вы писали:
HCI>Здравствуйте, Mike Chaliy, Вы писали:
MC>>Интерсно мнение, может ли идеологически сеттер кидать исключение?
HCI>Если говорить _идеологически_, то нет, исключение в сеттере — это некошерно.
Здравствуйте, _FRED_, Вы писали:
U>>Поэтому проверок в сеттерах быть не должно, проверки корректности состояния объекта должны производиться либо по месту действия, либо специальным методом вроде IsValidObject().
_FR>…и решение данной проблемы оказалось ортогональным валидации. То есть, получается, каждому уровню валидации — своё место и своё поведение, свой результат.
А по существу? Я привел самый простой случай взаимозависящих свойств. Покажи как решить задачу изменения взаимозависящих свойств, используя проверки в сеттерах (с использованием DependencyObject или нет не важно). Я решения этой задачи с использованием проверок в сеттерах не вижу.
_FR>И на частном примере "вот так вот плохо", вывод что "вообще нельзя" сомнителен.
Т.е. ты предлагаешь для невзаимозависящих свойств пользоваться проверками в сеттерах, а для взаимозависящих каким-то другим механизмом?
Иногда можно и так, но тут лежат грабли. Допустим были у нас невзаимозависящие свойства, мы написали такой код:
Country Country
{
set { this.country = value; }
}
double Age
{
set
{
if (value < 18)
throw new InvalidOperation();
}
}
//Использование
drinkPerson.Age = 20;
drinkPerson.Country = Country.Russia;
Все работает, все прекрасно. Однако завтра задача усложнилась, теперь нужно чтобы возраст был не менее 18, а не менее country.MinDrinkAge. И что теперь делать? Если напишем так:
Country Country
{
set
{
if (Age < value.MinDrinkAge)
throw new InvalidOperation();
}
}
double Age
{
set
{
if (value < Country.MinDrinkAge)
throw new InvalidOperation();
}
}
бросит исключение, если, к примеру, в старом состоянии страна была установлена в Usa, где возраст распития спиртных напитков 21, соответственно как изменять состояние объекта с такими проверками непонятно.
Поэтому от проверок в сеттерах в этом случае придется отказаться, введя функцию ValidateObject(), однако ранее написанный код:
Стал теперь работать неправильно, т.к. в нем вызова ValidateObject() нет. Т.е. изменение внутренней логики проверки валидности класса Person повлекло за собой необходимость изменения кода использующего этот класс. Это весьма сомнительное удовольствие, чреватое внесением багов при изменении кода.
гарантирует, что изменение внутренней логики проверки валидности класса Person не повлечет за собой необходимость изменения кода использующего этот класс.
_FR>Это, например, неплохо решено в DependencyObject…
Честно говоря, не понял, как DependencyObject решает проблему изменения взаимозависящих свойств.
U>>Хотя правильнее всего сеттеры вообще не писать, чем меньше в коде сеттеров, тем проще он читается.
_FR>Уже не раз обсуждалось, что immutable-модель плохо совместима с дизайнерами (за все дизайнеры на свете не поручусь, не знаю, но за дотнетовские — да) компонентов.
Т.е. дизайнеры вынуждают нас писать плохой код. Далее уже нужно смотреть, что для нас в конкретном случае важнее — более высокое качество кода или возможность использования дизайнера.
_FR>Даже структуры (вопреки рекомендациям), даже с перегруженными Equals\GetHashCode (вопреки здравому смыслу) разработчики FCL, , сделали изменяемыми (System.Drawing.Size и System.Windows.Size).
Т.е. разработчики заботливо разложили грабли. Им за это нужно поаплодировать?
Re[4]: Проверки в сеттерах приводят к дублированию кода
_FR>>Это, например, неплохо решено в DependencyObject…
U>Честно говоря, не понял, как DependencyObject решает проблему изменения взаимозависящих свойств.
Там это делаеться колбеками, типа как тока значение поменялось дергаеться метод. Ничем не отличаеться от проверки в сеттере. Было введено как костыль, так как значение свойства в DependencyObject можно устаналивать минуя сеттер.
Mike Chaliy wrote:
> Интерсно мнение, может ли идеологически сеттер кидать исключение?
Сеттер обязан кидать исключение, если какой-то аргумент может привести к
дальнейшей ошибке ПРЯМО СЕЙЧАС. Например, в аргументах передали какой-то
объект obj, а мы у этого объекта в сеттере забираем идентификатор или
значение obj.getId() — поэтому мы в самом начале сеттера ставим
Assert.notNull(obj) в случае приватного или внутреннего публичного
метода, или throw new IllegalArgumentException в случае внешнего
публичного метода (SJCP).
Проверки на диапазоны, допустимые значения, размеры и прочее НЕ должны
быть в сеттерах. Не забываем про принцип Single Responsibility
Principle, а также про то, что мы должны стремиться к тому, что весь
код, который может работать на основе публичного контракта класса должен
быть вынесен за пределы класса.
Поэтому мы имеем следующую схему:
1. Сущность, которая хранит значения и ловит уж совсем откровенные косяки.
2. Спецификация сущности. В спецификации мы задаем правила определяющие
валидность сущности. Правила могут быть как очень простыми (login !=
null), более сложными (match("\\w+", name), так и составными (password
== passwordConfirmation). Спецификация может храниться мильоном
способов: жестко-закодированном, во внешнем XML конфиге, в скрипте, в
виде аннотаций сущности и т.д.
3. Валидатор. Данный элемент перед сущность и ее спецификацию, и
начинает проверку. После чего выдает список несоответствий с кодами.
Для сущностей domain layer можно вызывать валидатор в DAO слое перед
сохранением и после загрузки сущностей и возбуждать исключение в случае
наличия хотя бы одной ошибки.
Posted via RSDN NNTP Server 2.1 beta
Re[5]: Проверки в сеттерах приводят к дублированию кода
Здравствуйте, Mike Chaliy, Вы писали:
MC>так как значение свойства в DependencyObject можно устаналивать минуя сеттер.
Точнее даже не так. Я вообще не знаю чтобы ипользовало этот бедный сеттер. Биндинг не использует, в постороении дерева тоже свойства не учавствуют. Вобщемто в DependencyObject сеттеры, это так — для красоты.
Здравствуйте, mazurkin, Вы писали:
M>Mike Chaliy wrote:
>> Интерсно мнение, может ли идеологически сеттер кидать исключение?
M>Сеттер обязан кидать исключение, если какой-то аргумент может привести к M>дальнейшей ошибке ПРЯМО СЕЙЧАС.
Угу, хороший ответ. Потдержка нескольких инвариантов обьекта.
Mike Chaliy wrote:
> M>Сеттер обязан кидать исключение, если какой-то аргумент может привести к > M>дальнейшей ошибке ПРЯМО СЕЙЧАС. > > Угу, хороший ответ. Потдержка нескольких инвариантов обьекта. > SJCP — это что?
Я хотел сказать, что вопрос про выбор альтернативы в виде assert'ов и
IllegalArgumentException'ов в методах задается в экзамене SCJP (Sun
Certified Java Programmer)
Здравствуйте, mazurkin, Вы писали:
M>Mike Chaliy wrote:
>> M>Сеттер обязан кидать исключение, если какой-то аргумент может привести к >> M>дальнейшей ошибке ПРЯМО СЕЙЧАС. >> >> Угу, хороший ответ. Потдержка нескольких инвариантов обьекта. >> SJCP — это что?
M>Я хотел сказать, что вопрос про выбор альтернативы в виде assert'ов и M>IllegalArgumentException'ов в методах задается в экзамене SCJP (Sun M>Certified Java Programmer)
Аха, я так и нашел, просто не мог связать.
А еще вопрос. Assert.notNull(obj) попадает в релизные билды?
Mike Chaliy wrote:
> А еще вопрос. Assert.notNull(obj) попадает в релизные билды?
Вообще вопрос совершенно неоднозначный и весьма интересный, по крайней
мере потому, что в свое время много над ним думал
Если мы обратимся к истории и вспомним MSVC+/Borland Delphi и иже с
ними, то увидим, что там существует четкое разделение release и debug
билдов. И в debug билдах ассерты действительно отключаются — в C++ через
макросы, в Delphi — вроде через сам язык.
Меня все время это очень удивляло. Я нахожу в этом некое недальновидное
лицемерие.
То есть мы поставляем клиенту продукт с отключенной системой внутренних
проверок в надежде на то, что если ошибки и возникнут, то в отсутствие
ассертов у нас всплывет не четкий и понятный баннер с информацией о том,
что мы облажались и где допустили такую ситуацию; а абстрактное окошко
об ошибке в модуле kerner32.dll и можно будет все списать на "глючную
винду", а то глядишь и все само собой рассосется. И я бы понял, если бы
отключение ассертов как то влияло на производительность, но ведь
банальные проверки на фоне других операций никаких задержек не вносят.
Посему я сторонник жесткой ответственности — внутренние проверки не
отрубать вообще никогда, кроме тех, что реально влияют на
производительность.
Здравствуйте, mazurkin, Вы писали:
M>Mike Chaliy wrote:
>> А еще вопрос. Assert.notNull(obj) попадает в релизные билды?
M>Вообще вопрос совершенно неоднозначный и весьма интересный, по крайней M>мере потому, что в свое время много над ним думал
M>Если мы обратимся к истории и вспомним MSVC+/Borland Delphi и иже с M>ними, то увидим, что там существует четкое разделение release и debug M>билдов. И в debug билдах ассерты действительно отключаются — в C++ через M>макросы, в Delphi — вроде через сам язык.
M>Меня все время это очень удивляло. Я нахожу в этом некое недальновидное M>лицемерие.
M>То есть мы поставляем клиенту продукт с отключенной системой внутренних M>проверок в надежде на то, что если ошибки и возникнут, то в отсутствие M>ассертов у нас всплывет не четкий и понятный баннер с информацией о том, M>что мы облажались и где допустили такую ситуацию; а абстрактное окошко M>об ошибке в модуле kerner32.dll и можно будет все списать на "глючную M>винду", а то глядишь и все само собой рассосется. И я бы понял, если бы M>отключение ассертов как то влияло на производительность, но ведь M>банальные проверки на фоне других операций никаких задержек не вносят.
M>Посему я сторонник жесткой ответственности — внутренние проверки не M>отрубать вообще никогда, кроме тех, что реально влияют на M>производительность.
Просто в .Нет когда говорят ассеры, обычно подразумеваться дебажные ассерты. Вот меня и заинтерисовал вопрос, как с этим доло обстоит в джаве. Быстрым поиском я этого не нашел, вот и решил спросить.
Mike Chaliy wrote:
> Просто в .Нет когда говорят ассеры, обычно подразумеваться дебажные ассерты. Вот меня и заинтерисовал вопрос, как с этим доло обстоит в джаве. Быстрым поиском я этого не нашел, вот и решил спросить.
Так я и хочу сказать — что независимо от платформы или языка считаю, что
ассерты не могут быть в каком-то отдельном релизе. Если уж поставили
ассерт, пусть он работает всегда! Или убедите меня в обратном
Здравствуйте, mazurkin, Вы писали:
M>Так я и хочу сказать — что независимо от платформы или языка считаю, что M>ассерты не могут быть в каком-то отдельном релизе. Если уж поставили M>ассерт, пусть он работает всегда! Или убедите меня в обратном
Я не очень понимаю в чем нужно убедить? Если я ставлю ассерт значит я хочу чтобы он работал только в дебаге. Если нужен аналог для всех релизов — я кидаю исключение.
Здравствуйте, Ziaw, Вы писали:
Z>Здравствуйте, mazurkin, Вы писали:
M>>Так я и хочу сказать — что независимо от платформы или языка считаю, что M>>ассерты не могут быть в каком-то отдельном релизе. Если уж поставили M>>ассерт, пусть он работает всегда! Или убедите меня в обратном
Z>Я не очень понимаю в чем нужно убедить? Если я ставлю ассерт значит я хочу чтобы он работал только в дебаге. Если нужен аналог для всех релизов — я кидаю исключение.
Я видел схемы именования дизана по контракту ввиде Ассерт.АргументНотНулл(). Ассерт же не переводится как "проверка для дебаггера", Ассерт перводиться как "Утверждение". Это как раз то чем занимаеться дизаин по контракту. А тот в свою очередь обязателен в любом релизе.
Собсно мой вопрос был в том присутвует ли эта конструкия в джаве в релизе.
Судя по этому:
Assists in validating arguments.
The class is based along the lines of JUnit. If an argument value is deemed invalid, an IllegalArgumentException is thrown.
Ziaw wrote:
> Я не очень понимаю в чем нужно убедить? Если я ставлю ассерт значит я хочу чтобы он работал только в дебаге. Если нужен аналог для всех релизов — я кидаю исключение.
Я не совсем это имел в виду.
Мне интересна мотивация — вот вы хотите чтобы ассерт работал только в
дебаге — а почему?
... MC>Я видел схемы именования дизана по контракту ввиде Ассерт.АргументНотНулл(). Ассерт же не переводится как "проверка для дебаггера", Ассерт перводиться как "Утверждение".
Утверждение, которое должно выполняется всегда.
MC>Это как раз то чем занимаеться дизаин по контракту.
Выполнение таких утверждений — часть контракта.
MC>А тот в свою очередь обязателен в любом релизе.
Обязательно выполнение контракта => обязательно выполнение таких утверждений(а не наличие assert'ов).
Здравствуйте, Юрий Жмеренецкий, Вы писали:
ЮЖ>Здравствуйте, Mike Chaliy, Вы писали:
ЮЖ>... MC>>Я видел схемы именования дизана по контракту ввиде Ассерт.АргументНотНулл(). Ассерт же не переводится как "проверка для дебаггера", Ассерт перводиться как "Утверждение". ЮЖ>Утверждение, которое должно выполняется всегда.
MC>>Это как раз то чем занимаеться дизаин по контракту. ЮЖ>Выполнение таких утверждений — часть контракта.
MC>>А тот в свою очередь обязателен в любом релизе. ЮЖ>Обязательно выполнение контракта => обязательно выполнение таких утверждений(а не наличие assert'ов).
Здравствуйте, Mike Chaliy, Вы писали:
... MC>Нифига не понял...
Предикаты, которые проверяет ассерт, в Desing by Contract являются способом выражения некоторых аспектов контракта. Контракт при этом должы соблюдать как клиент так и поставщик [какой-либо еденицы функциональности]. Разделение на клиента и поставщика необходимо для того, чтобы однозначно идентифицировать место нарушения(баг) контракта и виновника, т.е. ответственного за исправление(безусловное) этого бага.
Одиним из аспектов контракта являются предусловия — условия, которые обязан выполнить клиент, и если клиент их обеспечил, то поставщик гарантированно(здесь есть оговорки) выполнит свою работу ( = выполнит постусловия).
В этом месте люди делятся на два лагеря:
Для одних предусловия — это то что написано выше, для других — то же самое, но с одной оговоркой: 'клиенты иногда могут не выполнять предусловия'. Эта оговорка привоит к тому что поставщики кидают исключения при нарушениях предусловий клиентами.
Одно слово 'иногда' порождает очень существенные различия: у первых контракт выполняется всегда, а у других — иногда. В этом месте второй лагерь не верит первому про 'всегда', а первый... отключает ассерты в релизе . Им они незачем — выполнение контракта они обеспечили.
Здравствуйте, Юрий Жмеренецкий, Вы писали:
ЮЖ>Одно слово 'иногда' порождает очень существенные различия: у первых контракт выполняется всегда, а у других — иногда. В этом месте второй лагерь не верит первому про 'всегда', а первый... отключает ассерты в релизе . Им они незачем — выполнение контракта они обеспечили.
ЮЖ>Вот примерно так.
Клево написано.
А как технически реализована гарнтия того что клиент выполняет условия? И почему ассерты отрубаються в поставщике, а не в клиенте?
Здравствуйте, mazurkin, Вы писали:
M>Мне интересна мотивация — вот вы хотите чтобы ассерт работал только в M>дебаге — а почему?
Разрешите вставить свои 5 копеек.
При разработке, у меня возникает необходимость (или скорее желание) использовать как Debug.Assert'ы, которые будут отключены в релизной версии, так и утверждения которые в ней останутся. А также и проверки предусловий, которые мы тоже оставляем в релизной версии.
Почему есть желание использовать Assert только в Debug версии? Потому что, в нем можна использовать ресурсоемкие вычисления для проверки. А главное, Assert, с моей точки зрения, действительно никогда не дожен стрелять. Чего не скажешь о предусловиях. Т.е Assert я использую для проверки собственной логики, а специальные классы предусловия (Precondition) и утверждения (Ensure) для проверки внешней по отношению к классу логики, за которую я (класс) отвечать не могу. Но я (опять же класс) могу предъявить контракт в виде предусловий и/или предположений только при соблюдении которых я соглашусь что-то делать.
Попробую на примере лучше объяснить свои мысли.
public class OrderRepository
{
public IList<Order> GetClientOrders(Client client)
{
// Здесь элементарное предусловие. Метод не знает что ему делать с
// наловым клиентом. Вот и нечего такие данные передавать.
Precondition.Argument.NotNull(client, "Client");
// Здесь проверяется предусловие, что клиент уже был сохранен в БД.
// А иначе что искать?
Precondition.Argument.Check(IsPersisted(client, "Client is saved in database"));
return SomeORM.CreateQuery("LoadClientProducts ?").SetInt(client.Id).List<Order>();
}
public Order FindLastClientOrder(Client client)
{
// А здесь мне было лень писать предусловие. Я понадеялся, что они и так
// стрельнут в методе GetClientOrders. Наверное так делать не стоит.
IList<Order> clientOrders = GetClientOrders(client);
if (clientOrders.Count == 0)
{
return null;
}
Order result = clientOrders[clientOrders.Count - 1];
// Здесь ассерт документирует предположение, что последний элемент
// в списке заказов клиента обязательно имеет максимальную дату заказа.
// Также он указывает, что именно гарантирует правильный порядок сортировки
// (sql запрос или объектно реляционный преобразователь)
// Я хотел показать, что в предложении Assert можно не бояться использовать
// ресурсоемкие операции. Ведь в релизной версии их не будет.
// Сортировка делается один раз, на стороне сервера. Проверка может быть выполнена
// с помощью юнит тестов. Т.е. Assert здесь лишь способ документирования.
Debug.Assert(
new List<Order>(clientOrders).TrueForAll(x => x.Date < result.Date),
"Last order in the list has the greatest date",
"List is sorted in sql query");
return result;
}
}
class ProductRepository
{
public Product GetById()
{
return null;
}
public Product FindByBarcode(string barcode)
{
// Таким образом проверяется предусловие. Методу нельзя передавать
// null. Потому что он не знает что с этим null делать. Трактовать
// его как пустую строку или использовать как значение NULL сервера БД?
// Это не в компетенции данного метода. О чем мы и спешим сообщить миру
// посредством специального исключения.
Precondition.Argument.NotNullOrEmpty(barcode);
IList<Product> result = SomeORM
.CreateQuery("LoadProductByBarcode ?")
.SetString(0, barcode)
.List<Order>();
if (result.Count == 0)
{
return null;
}
// Эта проверка гарантирует, что запрос вернул только одно значение.
// И аргументирует это предположение тем, что штрих-код товара -
// это естественный ключ в БД.
// Идиологически не использую Assert, потому что структура БД мне
// не подвластна (по крайней мере с высоты этого кода, то что я мог
// создавать эту таблицу еще вчера, ничего не значит).
// Поэтому здесь используется утверждение в стиле design by contract, в том смысле
// что эта проверка будет жить и в релизной версии.
Ensure.That(
result.Count == 1,
"Only one item found",
"Barcode is an unique key in database");
return result[0];
}
public Product GetByBarcode(string barcode)
{
Precondition.Argument.NotNullOrEmpty(barcode);
Product product = FindByBarcode(barcode);
if (product == null)
{
// Такой вариант удобен когда клиент (класса) получил значение
// штрих-кода из доверительного источника, и хочет по нему
// гарантировано найти продукт. И чего он меньше всего ожидает,
// так это null в результате. То есть это действительно исключительная
// ситуация.throw new ArgumentException("Product with the specified barcode is not found");
}
return product;
}
public void Save(Product product)
{
Precondition.Argument.NotNull(product, "product");
CheckCanBeSaved(product);
// Save.
}
private static void CheckCanBeSaved(Product product)
{
// Так как это приватный метод, то ни о каком предусловии здесь
// речь не идет. Здесь может быть проверена лишь логика класса
// Я могу утверждать что здесь не null, потому что публичные методы
// обязаны сделать такую проверку.
Debug.Assert(product != null, "Product not null", "Checked in public methods");
// Check that product can be saved in db.
}
}
Таким образом мы используем разные типы проверок в разных случаях.
Precondition для проверки предусловий методов. Отключать их нельзя. Ведь я не могу гарантировать что клиентский код будет использовать метод корректно. Тем более это актуально для быблиотечных функций.
Ensure — для утверждений в смысле Design by contract, тех которые целесообразно оставить и в релизной версии программы.
Assert — в традиционном смысле, для проверки собственной логики класса, которая не зависит от внешних источников.
Exception — для исключительных ситуаций.
Пример немного надуман, что бы лучше продемонстрировать различные варианты использования проверок.
vansha wrote:
> Почему есть желание использовать Assert только в Debug версии? Потому > что, в нем можна использовать ресурсоемкие вычисления для проверки. А > главное, Assert, с моей точки зрения, действительно никогда не дожен > стрелять. Чего не скажешь о предусловиях. Т.е Assert я использую для > проверки собственной логики, а специальные классы предусловия > (Precondition) и утверждения (Ensure) для проверки внешней по > отношению к классу логики, за которую я (класс) отвечать не могу. Но > я (опять же класс) могу предъявить контракт в виде предусловий и/или > предположений только при соблюдении которых я соглашусь что-то > делать.
Большое спасибо за развернутый ответ. Но я все равно полностью не
получил желаемого
Насчет ресурсоемких проверок я согласился с вами авансом — несколькими
постами ранее.
Но зачем вы отключаете проверку собственной логики в релизе? Я не
понимаю. Вы боитесь, что написали логику неправильно и клиент об этом
узнает или что?
Здравствуйте, Mike Chaliy, Вы писали: MC>А как технически реализована гарнтия того что клиент выполняет условия? И почему ассерты отрубаються в поставщике, а не в клиенте?
Не думаю, что тебе удастся получить детали. В прошлый раз обсуждение с ЮЖ темы контрактов кончилось ничем. Его познания теории так глубоки, что никакой практике они не соответствуют.
... << RSDN@Home 1.2.0 alpha rev. 677>>
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, mazurkin, Вы писали:
M>vansha wrote:
M>Но зачем вы отключаете проверку собственной логики в релизе? Я не M>понимаю. Вы боитесь, что написали логику неправильно и клиент об этом M>узнает или что?
Скорее наоборот . Я отключаю проверки в релизе не потому что боюсь показать клиенту, что логика приложения не верна. Наоборот, я не боюсь отключить проверки в релизе, потому что после тестирования, уверен в том, что логика верна.
Мое восприятие таково. Нарушение условия в Assert, указывает, что в локальном коде разработчиком допущен баг (не ошибка с точки зрения пользователя), неверное предположение. Такое нарушение исправляется сразу же в процессе кодирования или при тестировании.
Нарушения условий, которые мы проверяем с помощью нашей реализации предусловий (Precondition) и утверждений (Ensure), также указывают на баг в программе. Но уже не в локальном коде класса, а коде клиентов класса. Т.е. на границе классов и компонентов. Такие ошибки протестировать сложнее. Поэтому такие проверки мы оставляем и в релизе. Так как лучше уж получить исключение (пусть не ласковое к пользователю), чем продолжать исполнение программы в некорректных условиях, с неизвестными последствиями.
Приведу навскидку еще один пример из кода
public static string DomainUserName
{
get
{
Ensure.That(
!String.IsNullOrEmpty(Environment.UserDomainName),
"Domain or local computer name defined",
"It seems to be always defined, even for local user that is not in domain");
Я благодарен разработчику кода, за то, что он сделал для меня очевидным факт, что метод Environment.UserDomainName всегда вернет не пустую строку. И мне не нужно смотреть документацию. А включать ли эту проверку и в релизе? Необязательно, так как это проверенно в процессе тестирования. (Хотя в данном случае включена в релизе, така ка используется класc Ensure).
vansha wrote:
> Скорее наоборот . Я отключаю проверки в релизе не потому что боюсь > показать клиенту, что логика приложения не верна. Наоборот, я не > боюсь отключить проверки в релизе, потому что после тестирования, > уверен в том, что логика верна.
Все понятно, но все равно не понятно зачем же отключать ассерты
То есть ваш ход мыслей я конечно понял — тут ничего спорного нет, но не
понял на чем основывается ваша уверенность в том, что логика верна
полностью. Тут (обращаясь к вашим словам) видимо вопрос именно веры или
ответственности.
Вы уверены, что после тестирования ваш код идеально правилен, а я — нет.
Даже при тотальном покрытии кода юнит-тестами остается осадочек, что
где-то мы чего-то могли забыть — не учесть какую-то ситуацию, в которой
при валидных с нашей точки зрения исходных данных результат работы
алгоритма становится неверен.
Еще другими словами. Предлагаю рассмотреть ситуацию с отключением
ассертов в релизе в разрезе такого анализа:
-----
1.1 Выгода, получаемая при отключении ассертов в релизе
1.2 Недостатки, устраняемые при отключении ассертов в релизе
2.1 Выгода, теряемая при отключении ассертов в релизе
2.2 Недостатки, приобретаемые при отключении ассертов в релизе
-----
Пока сам могу обозначить такие пункты
1.1.1 (rank=0) Уменьшение размера кода (кого это сейчас волнует?)
1.1.2 (rank=low) Увеличение быстродействия при отключении медленных
проверок (медленных проверок обычно крайне мало)
2.1.1 (rank=med) Мы теряем возможность поймать и затем устранить наши
редкие труднообнаружимые внутренние ошибки, не найденные при тестировании.
2.2.1 (rank=high) Есть риск, что не обнаруженная в ассерте ошибка может
нанести большой ущерб — то есть как сбой данная ситуация может себя не
проявить, а проявить намного позже — например неверной цифрой в годовом
отчете компании.
Здравствуйте, Mike Chaliy, Вы писали:
MC>Здравствуйте, Юрий Жмеренецкий, Вы писали:
ЮЖ>>Одно слово 'иногда' порождает очень существенные различия: у первых контракт выполняется всегда, а у других — иногда. В этом месте второй лагерь не верит первому про 'всегда', а первый... отключает ассерты в релизе . Им они незачем — выполнение контракта они обеспечили.
ЮЖ>>Вот примерно так.
MC>Клево написано.
MC>А как технически реализована гарантия того что клиент выполняет условия?
В этом вся проблема. Здесь требуется 100% покрытие тестами всех ассертов, что малореально и приводит к удорожанию разработки. Очевидный вывод — уменьшить количество ассертов(с сохранением общих инвариантов) насколько это возможно. Действенный способ: перенос проверок с run-time в compile-time.
А positive_float имеет инвариант — 'содержит неотрицательное число'. Теперь вместо того чтобы писать тесты на каждый случай использования sqrt, нужны тесты проверяющие моменты создания объектов positive_float.
При разрастании масшабов сразу появляются границы подсистем на которых статический контроль не работает. Эти места — места получения данных извне(в широком смысле слова), здесь проверки должны быть и должны тестироваться. Все это масшабируется от функций до сервисов и др. более крупных единиц функциональности.
Если посмотреть под другим угом, то можно заметить что вынесение предусловия(в примере) в инвариант positive_float есть ни что иное как следование SRP. С этой стороны DbC поощеряет 'правильный' дизайн.
+ Административные меры.
MC>И почему ассерты отрубаються в поставщике, а не в клиенте?
На предусловия поставщика ассерты есть только в поставщике. Это предусловия для его использования. А вырубаются они везде в релизе.
Здравствуйте, Sinclair, Вы писали:
S>Здравствуйте, Mike Chaliy, Вы писали: MC>>А как технически реализована гарнтия того что клиент выполняет условия? И почему ассерты отрубаються в поставщике, а не в клиенте? S>Не думаю, что тебе удастся получить детали.
Нахождение на RSDN у меня — далеко не перманентное состояние, поэтому и отвечаю я по мере возможностей.
Здравствуйте, mazurkin, Вы писали:
M>Все понятно, но все равно не понятно зачем же отключать ассерты
Ну вот. Скатываемся к дискуссии . Как мне кажется, у нас не совсем присутствует предмет спора .
Я полностью поддерживаю вашу точку зрения в том, что желательно как можно раньше получить исключение в случае если программа находится в некорректном состоянии. Поэтому необходим какой то механизм проверок, который бы работал и в релизе. Но я испытываю большие сомнения, что для этих целей следует использовать ассерт. Потому что он изначально предназначен для использования в debug версии.
Пример 1: Диалог который показывается пользователю при нарушении условия в ассерт. С кнопочкой [Пропустить].
Допустим есть у меня такой код:
Debug.Assert(obj != null, "obj is not null")
Conole.WriteLine(obj.ToString())
При obj == null, пользователь посмотрит непонятный диалог, нажмет пропустить, и продолжит формирование годового отчета.
Я осознаю, что это решается, путем переопределения TraceListener.
Пример 2: Понимание Assert коллегами. Как мне кажется, большинство разработчиков склонны воспринимать Assert, все-таки как средство отладки. Поэтому можно будет столкнуться с кодом на подобии этого:
Debug.Assert(
TestDatabaseUsed,
"Test database used",
"As we're in debug mode, we must use test database only.")
В этом случае assert как раз раз будет стрелять только в релизе, при использовании продакшн базы данных.
Опять же, конечно пример высосан из пальца. И проблема решается путем введения стандарта методов работы с ошибками.
Вопрос в том, нужно ли решать эти проблемы? Зачем для проверок, которые должны работать всегда, использовать механизм, который для этого не предназначен. Для себя, мы этот вопрос мы решили. Используем собственную реализацию механизмов проверки, которая предусматривает возбуждение честных исключений в случае если условие проверки нарушено. Сайд-эффектом такого подхода, является то, что мы можем использовать более гибкие и читабельные конструкции в коде. Пример использования я приводил несколькими постами выше.
При этом я продолжаю широко использовать Assert для целей отладки или документирования. Хотя наверное вру . Я так же как и Вы, считаю, что лучше перебдеть чем недобдеть, и большинство проверок я оставляю в релизе. Просто для этого использую не Assert, а наш собственный аналог. Например для процедуры формирования отчета:
Ensure.That(
IsValidForYearReport,
"Data for year report is valid",
"Checked in some else method")
И предпочитаю assert в других случаях:
Debug.Assert(
False,
"Unreacheble code",
"Эй! Ты же еще не реализовал этот метод! И никак не мог сюда попасть.")
Должен признать, что в одним из наших проектов на Delphi используются ассерты в релизной версии. Но не скажу что мне это нравится. Я не испытываю большого удовольствия когда вижу в логах ошибку EAssertionException. Да это лучше чем если б программа молча продолжила выполнение, но скорее всего, если assert стреляет, то там должен быть exception.
С другой стороны когда стрельнул ассерт в Html Help Compiler, мы с коллегой не могли не удержаться от язвительного "Ну ребята, что же вы, даже ассерты не отключили".
PS. А теперь попробую ответить на вопрос кратко . Ассерты в релизе отключаю потому что они мне там не нужны. Те проверки которые там нужны, включены явно. Ассерт остается для использования в тех целях, для которых он был предусмотрен.
Здравствуйте, Aikin, Вы писали:
A>Привет. А не подскажешь откуда взят такой грамонтый фреймворк предусловий/постусловий? Нахрапом гугл не дался
Ну, вряд ли это можно назвать фреймворком . Это на самом деле, простая реализация, которую мы (на работе) сделали для своих нужд. Постусловий как раз там и нету, как и инвариантов. То есть, это не есть реализация концепций Design by Contract (хотя навеяно конечно Мейером). Просто более удобный синтаксис проверки условий, чем простое использование exceptions или assert.
Относительно такого фреймворка для .Net, я интересовался только nContract. Но пока не вижу нужды использовать что-то боле сложное чем несколько простых классов.
Здравствуйте, mazurkin, Вы писали:
... M>Вы уверены, что после тестирования ваш код идеально правилен, а я — нет. M>Даже при тотальном покрытии кода юнит-тестами остается осадочек, что M>где-то мы чего-то могли забыть
Это проблема идентификации пред и (особенно) постусловий. Предлагаю рассмотреть контракт функции добавления числа в некий Set (множество уникальных чисел):
Предусловия:
Есть два варианта — 'разрешить' вставку значения, которое уже присутствует или запретить.
Каждый имеет право на жизнь (иногдя и оба) и выбор зависит от ситуации, но мы пока запретим, т.е. предусловие будет следующим:
/// \pre: Exist(v) == false
void Insert(int v)
Постусловия — что должно выполняться после(!) вставки:
post: Размер увеличивается на 1. 'Size() = oldSize + 1'
Достаточно этого ? Очевидно что нет, т.к. такая реализация:
void Set::Insert(int v)
{
_set.Insert(Size()); // Для простоты - _set это уже готовое мн-во
}
Удовлетворяет нашему постусловию, но выполняет совсем не то что нужно.
Добавляем еще одно постусловие:
post: добавленный элемент действительно присутствует во множетсве — 'Exist(v) == true'
Достаточно этого или нет ? Нет. т.к. так нет гарантии что вставка (ошибочная реализация) изменит значения существующих элементов.
Необходимо добавить еще одно постусловие: Все значения, которые находились во множестве до вставки, остаются в нем и после вставки.
Итого:
void Set::Insert(int v)
{
Assert(Exist(v) == false);
//...
Assert(Size() == oldSize + 1);
Assert(Exist(v) == true);
Assert(Все значения, которые находились во множестве до вставки, остаются в нем и после вставки);
}
Дальше. Что должно происходить в случае если предусловия выполняются, а реализация по каким-либо причинам, от нее не зависящих(памяти, например не хватило), не может обеспечить выполнение постусловий ? А должно произойти исключение(или другая форма 'return error'). vansha в соседнем сообщении использует для этого Ensure.
Замена ассертов в постусловиях в ф-ии Insert на проверки вида:
if(Size() != oldSize + 1)
throw Exception();
— абсурд, т.к. во первых это означает что реализация не реализует то что должна(это ее обязанности), во вторых, постусловия могут проверятся во внешнем коде в unit-тестах:
Set s;
//...
s.Insert(v);
Assert(s.Size() == oldSize + 1);
Assert(s.Exist(v) == true);
Assert(Все значения, которые находились во множестве до вставки, остаются в нем и после вставки);
Так делают, когда объем проверок большой или проверки сложные, в случаях тестирования реализаций интерфейсов и т.п.
Похожие аналогии можно провести и для предусловий.
Здравствуйте, Юрий Жмеренецкий, Вы писали:
... ЮЖ>Похожие аналогии можно провести и для предусловий.
+ "но в этом случае проверки — часть production кода"
Здравствуйте, vansha, Вы писали:
V>Ну, вряд ли это можно назвать фреймворком . Это на самом деле, простая реализация, которую мы (на работе) сделали для своих нужд.
Я так и подумал, когда инет отказался выдать инфу по такому синтаксису
Меня именно синтаксис задел. Уж очень он говорящий.
А ты не мог бы поделится либо самой dll-кой либо списком классов-методов которые вы используете?
V>Но пока не вижу нужды использовать что-то боле сложное чем несколько простых классов.
Возможно, но так как я в DbC не силен, то хотелось бы получить готовую реализацию пред/постусловий чисто чтобы "набрал 'Precondition' нажал 'точку'" и смотри какие контракты есть, выбирай наиболее близкий...
Здравствуйте, Юрий Жмеренецкий, Вы писали:
MC>>А как технически реализована гарантия того что клиент выполняет условия? ЮЖ>В этом вся проблема. Здесь требуется 100% покрытие тестами всех ассертов, что малореально и приводит к удорожанию разработки.
Хм, каких ассертов? Клиентских чтоли? Ок, я был не уверен говорити ли вы про границы компонентов. Судя по всему не говорите. Речь идет тока про код, клиенты которого гарантированно не поменяються. Я придерживаюсь мнения что такого не бывает. Относительго гибкие дизаны, подразумевают что компонент это черный ящик. Он должен гарантированно правильно работать со всеми своими клиентами. Если не может — сообщить об этом. Соотвевенно в релизе эти проверки должны остаться.
ЮЖ>Очевидный вывод
Да это очевидный, жаль что Майкры прикрыли работу в этом направлении, оставив в Шарпе 4.0 костыли... Это я про SpecSharp
ЮЖ>При разрастании масшабов сразу появляются границы подсистем на которых статический контроль не работает. Эти места — места получения данных извне(в широком смысле слова), здесь проверки должны быть и должны тестироваться. Все это масшабируется от функций до сервисов и др. более крупных единиц функциональности.
Угу, вопрос перемещаеться в плоскость что называть границами... Для меня например граница это все публичное или интернал. А соотвевеннло у меня все(после всех очевидных комилтай м проверок) проверки остаються в релизе.
Здравствуйте, Mike Chaliy, Вы писали:
... MC>Речь идет тока про код, клиенты которого гарантированно не поменяються. Я придерживаюсь мнения что такого не бывает.
Я на это смотрю более радикально — любой код меняется(произойдет ли это на самом деле — второстепенно).
MC>Относительго гибкие дизаны, подразумевают что компонент это черный ящик.
Реализация — несомненно черный ящик, но публичный контракт, наоборот — является(должен по крайней мере) четко определенным. От компонента(некой единицы функциональности) мне важно что он делает(контракт), а не как(реализация). Здесь кстати есть место для ошибок — протаскивание лишних деталей реализации в контракт.
MC>Он должен гарантированно правильно работать со всеми своими клиентами.
Сначала отвечу на это:
MC> вопрос перемещаеться в плоскость что называть границами...
В моем варианте: границы это места "обрастания" различных сырых данных инвариантами. Таких мест достаточно мало — user input, чтение из файлов, прием данных по сети, и т.п. Можно еще добавить отложенные инварианты и кривые api.
У всех эти "генераторов" данных есть один общий элемент — отсутствие(полное или частичное) постусловий, которые говорят о содержимом. Для некоторых это обусловленно их дизайном — в "gereric" text box можно вводить не только возраст(min-max) а произвольное число и даже текст. И только потом(в gui framework или явно) производится заточка под возможность ввода только валидного, например возраста. С другой стороны — их природой: нет гарантии что из файла мы прочитаем то же что и записали. Вероятность порчи данных можно снизить с помощью привелегий, административным путем, но все равно 100% гарантию мы не обеспечим. Некоторую гарантию можно дать для БД, но до тех пор, пока в таблицы не лезут руками.
Вот это для меня границы. Весть такой "ввод" проверяется явно(благо мест очень мало) и в дальнейшем данные(валидные) свободно гуляют в системе. В первом приближении это похоже на 'если объект существует, то он находится в валидном состоянии', positive_float помните?
В эту схему органически вписываются и данные для которых инварианты не известны или являются взаимоисключащими в различных сценариях. Полноценными "объектами"(это не обязательно объекты с точки зрения языка) они становятся в момент соответствующих проверок. Или не становятся — определяется, опять же, результатом проверок.
Вот эти проверки — никогда не удаляются из релиза. Удаляются только проверки(это уже ассерты) "внутри" кода, который использует проверенные данные. Эти ассерты просто наблюдают за состоянием программы при отладке/тестировании, помогая обноружать баги, допущенные при изменениях кода, причем эти баги вызваны нарушениями контрактов и должны быть исправлены, т.к. работу программы вне рамках контракта гарантировать никто не будет.
Unit-тесты — это по сути теже самые ассерты, только вынесеные в отдельное окружение. Почему-то для них вопроса 'включения в релиз' не стоит.
Теперь возвращаясь к: MC>Он должен гарантированно правильно работать со всеми своими клиентами.
У него есть контракт, пусть клиенты его соблюдают, тогда и он будет правильно работать.
Хотя это похоже на подход "от реализации" — компонент главный, а пользователи — второстепенны. Это не плохой, не хороший подход — он просто другой. При тотальном использовании Design by Contract пляшут от требований — начиная от высокоуровневых use cases(это тоже форма контрактов, причем образующая) и углубляясь по "самые помидоры".
Подход "от реализации" — это написание больших библиотек, фреймворков, вспомогательных сервисов и т.п. Основная сложность — частичная неизвестность сценариев использования, наличие некоторых предположений и обобщений. Это обычно приводит к тому, что внешний интерфейс(библиотеки/фреймворка/итп) или его части должны рассматриваться в качестве границы(см. выше) как клиентом, так и реализацией (например winapi функции проверяют свои парамеры на недопустимые значения).
Ну и никто не запрещает(после определения внешнего интерфейса) при реализации использовать DbC.
MC>Соотвевенно в релизе эти проверки должны остаться.
Только на границах.
Но если идти от реализации снизу вверх, и каждый модуль(или функцию) рассматривать как совершенно обособленную, то да — она имеет границу на входе и в точках использования других функций, а те в свою очередь... и т.д. рекурсивно.