Собственно, несколько лет назад разобрался с ними и хотел было юзать на всю. Но как то тормозило все да, по моему вспечатлению, не получили массового распространения. Вроде бы обещали добавить поддержку на уровне языка, емнип.
Как сейчас? Использовать старые добрые исключения или же переходить на сабж?
Здравствуйте, Shmj, Вы писали:
S>Как сейчас? Использовать старые добрые исключения или же переходить на сабж?
Смотря для чего.
Для большинства проектов для рантайм-проверок проще написать что-то своё. Решение бросать (по умолчанию) неотлавливаемый ContractException очень больно выстреливает по коду. В конечном итоге часть вызовов обкладывается catch{}, что приводит к куда более экзотическим багам, чем если бы контрактов не было бы вообще.
С другой стороны, в code contracts есть куча очень приятных штук для отладки, те же инварианты экономят кучу времени. Но использовать их вместе с самописными проверками (см предыдущий абзац)… это уже перебор.
Ну и наконец самое интересное: статическая верификация. В теории это то, что нужно — экономит время на отладку, совместимо с классическими проверками if-then-throw и в последних версиях почти допилили вывод контрактов, т.е. не нужно копировать их во все перегрузки. На практике всю идею убивает неимоверно долгое время сборки, до нескольких десятков минут. И неимоверная капризность солвера.
Сплошь и рядом проверку приходится переписывать, например, менять ИЛИ на И (с инверсией условий конечно). Или наоборот. Никаких явных закономерностей в этом нет, просто после обновления солвер внезапно начинает находить ложные срабатывания или не срабатывает при явном нарушении контракта. В общем, на поддержку в актуальном состоянии тратится куча лишнего времени. Добавляем сюда никакую активность в оф.репо, никакую поддержку на форуме и явный синдром NIH у авторов. Короче, нужно быть в очень отчаянном положении чтобы всерьёз рассчитывать на пользу от статической верификации в code contracts в текущем виде.
Лет пять назад у меня было побольше оптимизма, но положение за это время не поменялось практически никак. Если у кого есть положительный опыт — было бы очень интересно послушать
Здравствуйте, Shmj, Вы писали:
S>Собственно, несколько лет назад разобрался с ними и хотел было юзать на всю. Но как то тормозило все да, по моему вспечатлению, не получили массового распространения. Вроде бы обещали добавить поддержку на уровне языка, емнип.
S>Как сейчас? Использовать старые добрые исключения или же переходить на сабж?
Исключения это часть контракта. А code contracts это в первую очередь инструмент для описания и контроля за строгим соблюдением.
Если у Вас нет паранойи, то это еще не значит, что они за Вами не следят.
Здравствуйте, TK, Вы писали:
TK>Исключения это часть контракта. А code contracts это в первую очередь инструмент для описания и контроля за строгим соблюдением.
К сожалению, нет. CC — это попытка натянуть contract-driven подход на неприспособленный к этому рантайм, систему типов и язык. Плюс сделанная очень малыми силами и людьми без опыта энтерпрайз-разработки.
В Code Contracts тонны принципиально неверных решений, которые никогда не сделает человек с опытом поддержки большого по объему кода. По умолчанию бросаются неотлавливаемые исключения, необходимость дублировать контракты в перегрузках и самое обидное — нестабильная работа статической верификации. От версии к версии в нём постоянно что-то ломается, т.е. нельзя просто обновить библиотеку и надеяться на отсутствие ложных срабатываний/не срабатываний.
В общем в теории оно как бы и да, на практике, без очень веских причин — нет.
S>Как сейчас? Использовать старые добрые исключения или же переходить на сабж?
Несколько раз пытался прикрутить CC к проектам от больших до мелких — ни разу не получил ожидаемого эффекта (я от них ожидаю что при правильно написанных контрактах юнит-тесты будут не нужны, только интеграционные). Имхо сама идея 5+, но без поддержки со стороны CLR довести до ума будет нереально.
Nemerle — power of metaprogramming, functional, object-oriented and imperative features in a statically-typed .NET language
Здравствуйте, xednay89, Вы писали:
X>Я всё жду, когда SergeyT. отпишется :)
Он недавно у себя в блоге писал о контрактах. В комментариях дискусс. Он по сути в одиночку сейчас это дело тянет.
Может, за прошедшие пару недель что-то изменилось. Было бы интересно послушать Сергея.
Здравствуйте, Shmj, Вы писали:
S>Собственно, несколько лет назад разобрался с ними и хотел было юзать на всю. Но как то тормозило все да, по моему вспечатлению, не получили массового распространения. Вроде бы обещали добавить поддержку на уровне языка, емнип.
S>Как сейчас? Использовать старые добрые исключения или же переходить на сабж?
Нет. Вместо проверки значения типа в рантайме лучше использовать в параметрах тип в котором недопустимые значения просто невозможны.
Здравствуйте, Poopy Joe, Вы писали:
PJ>Нет. Вместо проверки значения типа в рантайме лучше использовать в параметрах тип в котором недопустимые значения просто невозможны.
Метод принимает float от 0 до 5. Что за тип будет в параметрах.
Другой — от 0 до 20. Тоже что будет?
Здравствуйте, Doc, Вы писали:
Doc>Здравствуйте, Poopy Joe, Вы писали:
PJ>>Нет. Вместо проверки значения типа в рантайме лучше использовать в параметрах тип в котором недопустимые значения просто невозможны.
Doc>Метод принимает float от 0 до 5. Что за тип будет в параметрах. Doc>Другой — от 0 до 20. Тоже что будет?
Будет кривой дизайн. "float от 0 до 5" что-то означает. Допустим размер. Вот и надо передавать тип SizeOfSomething, который сам заботится о своем инварианте. Такой код будет понятнее разработчику, не требует контрактов и прочих костылей и его невозможно сломать неверным вызовом, так как код будет контролироваться компилятором. Если уж язык использует статическую типизацию, то надо пользоваться ее преимуществами.
Здравствуйте, Poopy Joe, Вы писали:
PJ>Будет кривой дизайн. "float от 0 до 5" что-то означает. Допустим размер. Вот и надо передавать тип SizeOfSomething,
Это мы только что ввели SizeOfSomething.NaS (not a size) и PositiveInfinity/NegativeInfinity? И пишем перегрузки операциий и компареров?
PJ>который сам заботится о своем инварианте.
И добавили проверки на null?
PJ>Такой код будет понятнее разработчику,
Это по классу то на дипазон?
PJ> не требует контрактов и прочих костылей и его невозможно сломать неверным вызовом, так как код будет контролироваться компилятором.
Переложили с больной головы на здоровую?
PJ> Если уж язык использует статическую типизацию, то надо пользоваться ее преимуществами.
Здравствуйте, _Raz_, Вы писали:
_R_>Это мы только что ввели SizeOfSomething.NaS (not a size) и PositiveInfinity/NegativeInfinity? И пишем перегрузки операциий и компареров?
Эээ... зачем бы? SizeOfSomething может содержать только значения от 0 до 5, ни при каких других условиях он не создастся.
Перегрузки операторов конечно надо. Потому что, если размер в метрах, то и складывать его можно только с размером в метрах, а не любым случайным float. Так же как и размер 1 метр и в 1000мм равны, не смотря на разные значения float.
_R_>И добавили проверки на null?
float value тип, таким же можно сделать и SizeOfSomething, либо использовать Optional<SizeOfSomething>. Вот чего точно не стоит делать, так передавать null.
_R_>Это по классу то на дипазон?
По классу на сущность. Если разработчик строит дизайн на диапазонах float, то тут случай серьезнее.
_R_>Переложили с больной головы на здоровую?
На компилятор. Надеюсь у него здоровая голова и самое главное железная.
_R_>Там где они действительно преимущества.
Это то самое место.
Здравствуйте, Shmj, Вы писали:
S>Собственно, несколько лет назад разобрался с ними и хотел было юзать на всю. Но как то тормозило все да, по моему вспечатлению, не получили массового распространения. Вроде бы обещали добавить поддержку на уровне языка, емнип.
S>Как сейчас? Использовать старые добрые исключения или же переходить на сабж?
Правда попробовал вначале сюда залить все полотно, но что-то больно сложно было редактировать.
Поэтому полноценный ответ здесь: Начать ли использовать контракты?.
Здравствуйте, Poopy Joe, Вы писали:
PJ> "float от 0 до 5" что-то означает. Допустим размер. Вот и надо передавать тип SizeOfSomething, который сам заботится о своем инварианте.
Т.е. называем каждый используемый вид значений своим типом? В результате получаем кучу дополнительного кода приведения одного к другому, сравнений, присвоений и т.д. Идея в принципе не лишена смысла, но реализация ее в стиле "новый тип на каждый чих" это будет страшно.
Здравствуйте, Doc, Вы писали:
Doc>Здравствуйте, Poopy Joe, Вы писали:
PJ>> "float от 0 до 5" что-то означает. Допустим размер. Вот и надо передавать тип SizeOfSomething, который сам заботится о своем инварианте.
Doc>Т.е. называем каждый используемый вид значений своим типом? В результате получаем кучу дополнительного кода приведения одного к другому, сравнений, присвоений и т.д. Идея в принципе не лишена смысла, но реализация ее в стиле "новый тип на каждый чих" это будет страшно.
А if на каждый чих это не страшно? А контракты на каждый чих не то же самое? Тот же самый код но в разных местах и в большем количестве.
Вместо того, чтобы компилятор проверял и гарантировал корректность использования типа лучше оставить это в кривых руках разработчика?
Здравствуйте, Poopy Joe, Вы писали:
PJ>А if на каждый чих это не страшно? А контракты на каждый чих не то же самое?
Нет, не тоже самое. В случае контрактов
*) Не надо писать код для SizeOfSomething + SizeOfSomething, SizeOfSomething + SizeOfSomething2 итд
*) Приведение SizeOfSomething к другим "физическим" типам
*) If для проверки вам не удастся привязать к интерфейсам (будут или вызовы вручную класса проверки или копипаста в каждую реализацию)
*) Не всегда диапазон зависит от физической величины. Метод может вполне иметь свои ограничения.
Здравствуйте, Doc, Вы писали:
Doc>Здравствуйте, Poopy Joe, Вы писали:
PJ>>А if на каждый чих это не страшно? А контракты на каждый чих не то же самое?
Doc>Нет, не тоже самое. В случае контрактов Doc>*) Не надо писать код для SizeOfSomething + SizeOfSomething, SizeOfSomething + SizeOfSomething2 итд
Надо! Или нет гарантии, что радианы не будут складываться с градусами, а размер головы с размером ноги.
Doc>*) Приведение SizeOfSomething к другим "физическим" типам
Что значит приведение? Другая величина это другая величина. Типа каста к float? Так весь смысл в том, чтобы этого не требовалось.
Doc>*) If для проверки вам не удастся привязать к интерфейсам (будут или вызовы вручную класса проверки или копипаста в каждую реализацию)
Не понял. Какой if, какой проверки? Интерфейс с типом SizeOfSomething не требует дополнительных проверок.
Doc>*) Не всегда диапазон зависит от физической величины. Метод может вполне иметь свои ограничения.
И? Допустим метод принимает возраст и требует, чтобы он был больше 18 лет. Тогда параметр будет например Adult<Age>. Вызываемый метод гарантированно имеет правильные значения, а вызывающие метод просто физически не сможет послать неверные данные, об этом заботится компилятор. А чем тут помогут контракты? Выкинут исключения во времени исполнения? Очень "полезно".
Здравствуйте, Poopy Joe, Вы писали:
PJ>И? Допустим метод принимает возраст и требует, чтобы он был больше 18 лет. Тогда параметр будет например Adult<Age>. Вызываемый метод гарантированно имеет правильные значения, а вызывающие метод просто физически не сможет послать неверные данные, об этом заботится компилятор. А чем тут помогут контракты? Выкинут исключения во времени исполнения? Очень "полезно".
Дык, Adult<Age> где-то будет из обычного Age получаться? В итоге всё равно без рантайм проверок не обойтись.
Здравствуйте, Poopy Joe, Вы писали:
PJ>Надо! Или нет гарантии, что радианы не будут складываться с градусами, а размер головы с размером ноги.
Вот как раз в вашем варианте это и надо. Например: HumanHeight = HeadHeigh + BodyHeight + LegHeight
В варианте с контрактами это будут три поля int или float и просто будет сумма. А гарантии будут в тестах.
Да, эти тесы и у вас будут, но к ним еще бонусом тесты приведения типов для сложения.
PJ>Что значит приведение? Другая величина это другая величина.
А как еще сложить HeadHeigh и BodyHeight?
Doc>>*) Не всегда диапазон зависит от физической величины. Метод может вполне иметь свои ограничения. PJ>И? Допустим метод принимает возраст и требует, чтобы он был больше 18 лет. Тогда параметр будет например Adult<Age>.
*) у вас класс Adult должен знать о Age, уметь извлекать из него значения. Лишний класс, лишние тесты, да еще и с жесткой связью.
*) Если у вас будут методы для "до 16", "до 18" и для всех. Будете еще классы создавать на каждый чих?
PJ>А чем тут помогут контракты?
Contract.Requires(18 < age);
PJ>Выкинут исключения во времени исполнения? Очень "полезно".
А что сделает Adult<Age> если Age будет больше 18?
Кстати, исключение тут вполне уместно, т.к. скорее всего это означает что у вас как-то или малолетка "пролез" через авторизацию, или где-то ошибка в логике загрузки юзеров. Ведь для метода, например, логина такой контракт не уместен (получится что вы хотите BL в контракты зашить)
Здравствуйте, DarkEld3r, Вы писали:
DE>Здравствуйте, Poopy Joe, Вы писали:
PJ>>И? Допустим метод принимает возраст и требует, чтобы он был больше 18 лет. Тогда параметр будет например Adult<Age>. Вызываемый метод гарантированно имеет правильные значения, а вызывающие метод просто физически не сможет послать неверные данные, об этом заботится компилятор. А чем тут помогут контракты? Выкинут исключения во времени исполнения? Очень "полезно". DE>Дык, Adult<Age> где-то будет из обычного Age получаться? В итоге всё равно без рантайм проверок не обойтись.
Одна в любом случае необходимая проверка в момент создания типа, против множества проверок в разных местах.
Будучи созданным один раз этот тип остается валидным на протяжении всей жизни объекта.
...
var adults = ages.Where(IsLegalAdultAge).Select(a => new Adult(a));
var x = adults.Map(Func1)); // Зачем в Func1 может понадобится контракт?
...
Привет.
Нашел пару опечаток, без которых было бы лучше
Можно искать по ctrl+f и заменять на.
>Dопрос
Вопрос
>верифиатор
верификатор
>поноценные
полноценные
>конатрактов
контрактов
>серечера
реceчера
Здравствуйте, Doc, Вы писали:
Doc>Здравствуйте, Poopy Joe, Вы писали:
PJ>>Надо! Или нет гарантии, что радианы не будут складываться с градусами, а размер головы с размером ноги. Doc>Вот как раз в вашем варианте это и надо. Например: HumanHeight = HeadHeigh + BodyHeight + LegHeight Doc>В варианте с контрактами это будут три поля int или float и просто будет сумма. А гарантии будут в тестах.
Т.е. вместо проверки валидности типа на этапе компиляции ты предлагаешь выполнять проверку в рантайме и наедятся на полноту и корректность тестов? Здорово, чо.
Doc>Да, эти тесы и у вас будут, но к ним еще бонусом тесты приведения типов для сложения.
И что в варианте с контрактами мне помешает прибавить туда же и FootSize вместе/вместо LegHeight?
PJ>>Что значит приведение? Другая величина это другая величина. Doc>А как еще сложить HeadHeigh и BodyHeight?
Да так же как и складывают матрицы. Никому, в здравом уме, в голову не придется из-за перегрузки оператора держать их в float[]. Что в этом особенного?
Doc>*) у вас класс Adult должен знать о Age, уметь извлекать из него значения. Лишний класс, лишние тесты, да еще и с жесткой связью.
Что значит лишний класс? У нас есть какие-то ограничения по количеству классов?
Doc>*) Если у вас будут методы для "до 16", "до 18" и для всех. Будете еще классы создавать на каждый чих?
Эээ... зачем?
PJ>>А чем тут помогут контракты? Doc>Contract.Requires(18 < age);
Прелестно. А потом потребуется изменить возраст в зависимости от страны...
PJ>>Выкинут исключения во времени исполнения? Очень "полезно". Doc>А что сделает Adult<Age> если Age будет больше 18?
Вернет ошибку в момент создания, разумеется.
Doc>Кстати, исключение тут вполне уместно, т.к. скорее всего это означает что у вас как-то или малолетка "пролез" через авторизацию, или где-то ошибка в логике загрузки юзеров. Ведь для метода, например, логина такой контракт не уместен (получится что вы хотите BL в контракты зашить)
Исключения вообще не слишком уместны, так как прерывают логику в случайном месте, но это отдельный разговор. В данном случае с типами ошибка обнаружится гораздо раньше. "Загрузку юзеров" просто нельзя будет сломать, так как она может вернуть только правильный тип.
Строго говоря, контракты они не для обнаружения ошибок авторизации, а для контроля программиста. Ситуация когда нарушается Contract.Requires(18 < age) никогда не должна случится. Ради этого там и колхозят статический анализатор кода. В нем весь смысл. Непонятно только зачем нужны костыли, когда компилятор уже имеет тот же самый анализатор только на уровне типов. И гораздо лучше работающий.
Здравствуйте, Poopy Joe, Вы писали:
PJ>Одна в любом случае необходимая проверка в момент создания типа, против множества проверок в разных местах.
С этим не спорю. Не понравилось именно противопоставление типов и "рантайм проверкам", так как совсем без проверок всё-таки не обойтись.
Здравствуйте, Poopy Joe, Вы писали:
PJ>Т.е. вместо проверки валидности типа на этапе компиляции ты предлагаешь выполнять проверку в рантайме и наедятся на полноту и корректность тестов? Здорово, чо.
Ну если так параноить, то что помешает в LegHeight записать размер ноги?
Doc>>*) у вас класс Adult должен знать о Age, уметь извлекать из него значения. Лишний класс, лишние тесты, да еще и с жесткой связью. PJ>Что значит лишний класс? У нас есть какие-то ограничения по количеству классов?
Ну да, чем больше классов тем проще жить? Даешь Hello World из 100500 классов Как там "wow: Здорово, чо."
PJ>>>А чем тут помогут контракты? Doc>>Contract.Requires(18 < age); PJ>Прелестно. А потом потребуется изменить возраст в зависимости от страны...
Сравнивай не с константой. Да и мне кажется что опять начинаешь мешать контракты и бизнес-логику.
PJ>>>Выкинут исключения во времени исполнения? Очень "полезно". Doc>>А что сделает Adult<Age> если Age будет больше 18? PJ>Вернет ошибку в момент создания, разумеется.
И как у нас конструктор вернет ошибку? Или мы еще и factory с возвратом ошибок замутим?
PJ>Исключения вообще не слишком уместны, так как прерывают логику в случайном месте, но это отдельный разговор.
Ну во первых не в случайном месте, а вполне в конкретном.
Далее, в метод, где не может быть возраст меньше заданного пролез такой возраст. Значит программа уже в не консистентном состоянии. Так что исключению тут самое место.
Ну и опять ты путаешь BL и контракты метода.
Здравствуйте, Doc, Вы писали:
Doc>Здравствуйте, Poopy Joe, Вы писали:
Doc>Ну если так параноить, то что помешает в LegHeight записать размер ноги?
Тип и помешает. Если ты имел ввиду что мне помешает записать в LegHeight неверные данные по видом размера? То ответ — ничего, конечно. Как можно программисту помешать намеренно что-то сделать?! Речь о том, что программисту типы помогают не совершать ошибки.
Doc>Ну да, чем больше классов тем проще жить? Даешь Hello World из 100500 классов Как там "wow: Здорово, чо."
Да что значит больше/меньше? Откуда вообще это взялось? Ну давай все запихаем в один класс, так нормально будет?
Doc>Сравнивай не с константой. Да и мне кажется что опять начинаешь мешать контракты и бизнес-логику.
Почему? Это константа и есть.
Doc>И как у нас конструктор вернет ошибку? Или мы еще и factory с возвратом ошибок замутим?
Ну в примере выкинет исключение, но там это скорее перестраховка, а в обычном случае можно использовать статический метод и вернуть тип Either<Adult<Age>, Error>.
Doc>Ну во первых не в случайном месте, а вполне в конкретном.
С точки зрения перехватывающего метода — с случайном. Можно конечно на каждую операцию писать try/catch, но кто это будет читать потом?
Doc>Далее, в метод, где не может быть возраст меньше заданного пролез такой возраст.
Что значит "пролез"? Как он туда может пролезть?
Здравствуйте, Poopy Joe, Вы писали:
PJ>Речь о том, что программисту типы помогают не совершать ошибки.
Помогают. Но при этом, чем больше самого кода — тем больше вероятность ошибки.
Doc>>Ну да, чем больше классов тем проще жить? Даешь Hello World из 100500 классов Как там "wow: Здорово, чо." PJ>Да что значит больше/меньше? Откуда вообще это взялось? Ну давай все запихаем в один класс, так нормально будет?
Причем тут запихаем все в один? Но при этом же не надо плодить сущностей сверх меры. Понятно что у каждого она своя. Однако, создавать класс на каждый чих BL (там где вполне подходят стандартные), это перебор.
Doc>>Сравнивай не с константой. Да и мне кажется что опять начинаешь мешать контракты и бизнес-логику. PJ>Почему? Это константа и есть.
Определись, ты хочешь менять возраст или нет. )
Doc>>И как у нас конструктор вернет ошибку? Или мы еще и factory с возвратом ошибок замутим? PJ>Ну в примере выкинет исключение, но там это скорее перестраховка
Угу. Т.е. exception из контракта это ой ужас, а из конструктора — это прям прелесть
> а в обычном случае можно использовать статический метод и вернуть тип Either<Adult<Age>, Error>.
Т.е. как я сказал вместо 1 строки с float(или int), в месте которое хорошо покрывается тестами, ты делаешь уже Age, Adult<Age>, фабричный метод в статик классе, ну и общие Either и Error. Добавь тесты на все это добро.
Но главное, ты опять путаешь BL и контракты. Давай на примере покажу
Допустим у нас есть закрытая область (раз уж взяли это Age). Посмотрим на 2 метода
1) Login(User user). Тут просто обычная проверка user.Age и возврат кода. У этого метода не может быть в контракте 18 < user.Age, т.к. попытаться залогиниться у нас может любой юзер. Если что — вернем Error, HttpStatusCode.Forbidden или что еще там надо
2) GetShopContent(..., User user). А вот тут уже контракт что user обязан быть старше 18. И если сюда как-то попал младше, то как я сказал явно проблема в реализации BL. Поэтому кидаем исключение и ловим его на самом верху, программу закрываем, т.к. дальше уже работать нет по сути смысла.
PJ>С точки зрения перехватывающего метода — с случайном. Можно конечно на каждую операцию писать try/catch, но кто это будет читать потом?
Как я уже сказал, ловим такое на самом верху, сохраняем/отправляем что надо для дебага, завершаем работу.
Doc>>Далее, в метод, где не может быть возраст меньше заданного пролез такой возраст. PJ>Что значит "пролез"? Как он туда может пролезть?
Здравствуйте, Doc, Вы писали:
Doc>Помогают. Но при этом, чем больше самого кода — тем больше вероятность ошибки.
Кэп, ты? Больше то оно больше, только в одном случае ошибку можно сделать в одном, очевидном, месте, а в другом в куче мест.
Doc>Причем тут запихаем все в один? Но при этом же не надо плодить сущностей сверх меры. Понятно что у каждого она своя. Однако, создавать класс на каждый чих BL (там где вполне подходят стандартные), это перебор.
А причем тут 100500, которые ты выдумал? Откуда взялась какая-то мера? Как ты определяешь где перебор, а где нет? Типа в проекте уже слишком много классов будет пихать в существующие?
Doc>Определись, ты хочешь менять возраст или нет. )
18 это константа у тебя в тексте. Причем тут хочу я менять или нет? У меня логика проверки в одном месте у тебя во всех методах с контрактом.
Doc>Угу. Т.е. exception из контракта это ой ужас, а из конструктора — это прям прелесть
Ужас. Но там заведомо известно, что он не случиться.
Doc>Т.е. как я сказал вместо 1 строки с float(или int), в месте которое хорошо покрывается тестами, ты делаешь уже Age, Adult<Age>, фабричный метод в статик классе, ну и общие Either и Error. Добавь тесты на все это добро.
Фраза "хорошо покрывается тестами" ужасно смешная. Она покрывается тестами так как программисту кажется достаточным его покрыть, что, собственно, не гарантирует ничего, а в отличии от типов. Если весь метод MakeAdult состоит из одной проверки, то тесты там вообще не нужны. И кстати какие еще фабричные методы? Зачем они?
Doc>Но главное, ты опять путаешь BL и контракты. Давай на примере покажу Doc>Допустим у нас есть закрытая область (раз уж взяли это Age). Посмотрим на 2 метода Doc>1) Login(User user). Тут просто обычная проверка user.Age и возврат кода. У этого метода не может быть в контракте 18 < user.Age, т.к. попытаться залогиниться у нас может любой юзер. Если что — вернем Error, HttpStatusCode.Forbidden или что еще там надо Doc>2) GetShopContent(..., User user). А вот тут уже контракт что user обязан быть старше 18. И если сюда как-то попал младше, то как я сказал явно проблема в реализации BL. Поэтому кидаем исключение и ловим его на самом верху, программу закрываем, т.к. дальше уже работать нет по сути смысла.
Ты так и не понял о чем я говорю. Да, у тебя проблема в реализации BL, так как не может быть никакого User в GetShopContent с возрастом меньше 18, так как тип должен быть Adult<User> или Validated<User>. И в отличии от "проблема, исключение, креш, недовольный юзер" — это просто не может сломаться.
Контракты это способ искать где ты сломал логику, а типы это способ ее не ломать.
Doc>Как я уже сказал, ловим такое на самом верху, сохраняем/отправляем что надо для дебага, завершаем работу.
Ну т.е. в случайном месте прервалось выполнения, поэтому просто свалились. Так оно и происходит, я не понимаю почему ты это описываешь как что-то хорошее?
Doc>>>Далее, в метод, где не может быть возраст меньше заданного пролез такой возраст. PJ>>Что значит "пролез"? Как он туда может пролезть?
Doc>Вот именно. См чуть выше пункт (2).
Что вот именно? Это был вопрос? Как возраст меньше 18 может пролезть в Adult<Age>, чисто технически?
Здравствуйте, Poopy Joe, Вы писали:
PJ>А причем тут 100500, которые ты выдумал? Откуда взялась какая-то мера? Как ты определяешь где перебор, а где нет? Типа в проекте уже слишком много классов будет пихать в существующие?
Я не буду еще раз считать сколько ты уже привел классов для замены одной единственной строчки проверки.
Предложу тебе идти еще дальше и заворачивать каждый if в отдельный класс (жаль тут нет :irony: )
Doc>>Определись, ты хочешь менять возраст или нет. ) PJ>18 это константа у тебя в тексте. Причем тут хочу я менять или нет?
Ну замени 18 на вызов метода. Не пойму в чем сложность, что ты к замене значения пристал.
Doc>>Угу. Т.е. exception из контракта это ой ужас, а из конструктора — это прям прелесть PJ>Ужас. Но там заведомо известно, что он не случиться.
Так, я вызываю new Adult(new Age(5)) что будет? С чего тут Exception не случится? А что будет?
PJ> И кстати какие еще фабричные методы? Зачем они?
"а в обычном случае можно использовать статический метод и вернуть тип Either<Adult<Age>, Error>." (с) ты
PJ>Ты так и не понял о чем я говорю. Да, у тебя проблема в реализации BL, так как не может быть никакого User в GetShopContent с возрастом меньше 18, так как тип должен быть Adult<User> или Validated<User>. И в отличии от "проблема, исключение, креш, недовольный юзер" — это просто не может сломаться. PJ>Контракты это способ искать где ты сломал логику, а типы это способ ее не ломать.
Угу, только вот наворачивать класс на каждую проверку раздует код. Нет, я не понимаю когда такой класс обусловлен BL и используется повсеместно. Но делать на каждый чих? Вот у тебя есть Adult, а если завтра надо будет передать только пользователей мужского пола? Male? А если только взрослых мужчин — создаешь AdultMale? А если взрослых женщин с ростом более 170 — AdultFemaleWithMinHeight? Да еще по фабричному (стат.) методу на каждый?
Doc>>Как я уже сказал, ловим такое на самом верху, сохраняем/отправляем что надо для дебага, завершаем работу. PJ>Ну т.е. в случайном месте прервалось выполнения, поэтому просто свалились. Так оно и происходит, я не понимаю почему ты это описываешь как что-то хорошее?
Потому что у приложение уже ненормальное состояние. Выполнять его дальше нет смысла.
PJ>Что вот именно? Это был вопрос? Как возраст меньше 18 может пролез в Adult<Age> чисто технически?
Здравствуйте, Doc, Вы писали:
Doc>Я не буду еще раз считать сколько ты уже привел классов для замены одной единственной строчки проверки. Doc>Предложу тебе идти еще дальше и заворачивать каждый if в отдельный класс (жаль тут нет :irony: )
Одна единственная строчка? Да у тебя эти строчки будут размазаны по всему коду, во всех методах. Если надо проверить, что возраст соответствует легальному это должно быть сделано. Можно это сделать в типе, можно это сделать во всех методах контрактами или ifми или еще как. Ну будут огромные методы с кучей проверок, зато типов меньше, ага.
Doc>Ну замени 18 на вызов метода. Не пойму в чем сложность, что ты к замене значения пристал.
Ну замени и что у тебя получится? Либо тот же самый дополнительный тип, отвечающие за проверку возраста, либо логика размазанная по всей программе. И это вместо того, чтобы просто передать готовый тип...
Doc>Так, я вызываю new Adult(new Age(5)) что будет? С чего тут Exception не случится? А что будет?
К чему этот пример? Если человек хочет специально писать кривой код, то разумеется ничего ему не сможет помешать.
Doc>"а в обычном случае можно использовать статический метод и вернуть тип Either<Adult<Age>, Error>." (с) ты
А где тут фабрики?
Doc>Угу, только вот наворачивать класс на каждую проверку раздует код.
А просто проверки нет?
Doc>Нет, я не понимаю когда такой класс обусловлен BL и используется повсеместно. Но делать на каждый чих? Вот у тебя есть Adult, а если завтра надо будет передать только пользователей мужского пола? Male?
Вот завтра у меня будет Male и я просто удалю тип Adult и сделать тип Male и компилятор мне автоматически покажет все места где мне надо изменить логику. Либо вообще просто изменю тип Validated<User> добавив в него какие угодно ограничения.
А у тебя с контрактом User.Age > 18 надо найти все места и прописать все руками, добавив новое ограничение. Это конечно не увеличит ни код, ни требуемое время.
Doc>Потому что у приложение уже ненормальное состояние. Выполнять его дальше нет смысла.
Это я понял. Я не понял почему вообще возможность перехода приложения в ненормальное состояние у тебя допустима и это считается правильным?
PJ>>Что вот именно? Это был вопрос? Как возраст меньше 18 может пролез в Adult<Age> чисто технически? Doc>Вообще-то речь про контракты шла.
И сейчас, надеюсь, идет. С этого и разговор начался. Контракт проверят что сломано, тип гарантирует, что ничего сломать нельзя.
Здравствуйте, Poopy Joe, Вы писали:
PJ>Одна единственная строчка? Да у тебя эти строчки будут размазаны по всему коду, во всех методах.
Ну у тебя и фантазия. Если у тебя возраст проверяется в каждом метода то да, это основание его вынести в конкретный класс.
Если поглядеть на код, то контракты методов обычно 1 в 1 совпадают редко (ибо наборы параметров разные).
Doc>>Ну замени 18 на вызов метода. Не пойму в чем сложность, что ты к замене значения пристал. PJ>Ну замени и что у тебя получится? Либо тот же самый дополнительный тип, отвечающие за проверку возраста, либо логика размазанная по всей программе. И это вместо того, чтобы просто передать готовый тип...
Заменил, выйдет
Contract.Requries(this.GetMinAge() < age)
где тут доп типы?
Doc>>Так, я вызываю new Adult(new Age(5)) что будет? С чего тут Exception не случится? А что будет? PJ>К чему этот пример? Если человек хочет специально писать кривой код, то разумеется ничего ему не сможет помешать.
Стоп! Ты сам говорил про конструкторы, про исключения.
PJ>Вот завтра у меня будет Male и я просто удалю тип Adult и сделать тип Male и компилятор мне автоматически покажет все места где мне надо изменить логику. Либо вообще просто изменю тип Validated<User> добавив в него какие угодно ограничения.
Вот тут огромный LOL... посмешил.
Если Adult дает гарантию возраста при передаче в метод, то передача Validated вообще ничего не гарантирует. Что помешает создать Validated который содержит юзера-мужчину и передать в метод, который ожидает что юзер будет женщиной? Тип тот же. Компиляция пройдет на ура. Так что у тебя в Validated может быть любая комбинация условий, что никакой гарантии уже не дает.
PJ>А у тебя с контрактом User.Age > 18 надо найти все места и прописать все руками, добавив новое ограничение. Это конечно не увеличит ни код, ни требуемое время.
Зато метод определяет свой контракт, а не какой-то внешний Validated в котором может быть все что угодно. А после завтра вообще придет твой коллега и перепишет Validated на свой лад.
Doc>>Потому что у приложение уже ненормальное состояние. Выполнять его дальше нет смысла. PJ>Это я понял. Я не понял почему вообще возможность перехода приложения в ненормальное состояние у тебя допустима и это считается правильным?
Где я сказал что это допустимо и более того что это правильно. Прочитай что процитировал сам же "у приложение уже ненормальное состояние". Это не нормально, не допустимо и не правильно. Может быть из-за ошибки в коде или логики. Именно поэтому экстренное
PJ>И сейчас, надеюсь, идет. С этого и разговор начался. Контракт проверят что сломано, тип гарантирует, что ничего сломать нельзя.
Ну ты уже показал что ничего невозможного нет, заменив Adult на Validated :D
Здравствуйте, Doc, Вы писали:
Doc>Ну у тебя и фантазия. Если у тебя возраст проверяется в каждом метода то да, это основание его вынести в конкретный класс. Doc>Если поглядеть на код, то контракты методов обычно 1 в 1 совпадают редко (ибо наборы параметров разные).
Ничего не понял. Причем тут совпадение параметров? Если у тебя контракт декларируется только в одном месте, то нафиг тогда они тебе вообще нужны?
Doc>Заменил, выйдет Doc>Contract.Requries(this.GetMinAge() < age) Doc>где тут доп типы?
Тут код размазанный про всему приложению, что еще хуже. Какой-то класс ответственный за скажем за покупку еще и должен знать про возраст?
Отличный пример как не надо делать хоть с контрактами, хоть без.
Doc>Стоп! Ты сам говорил про конструкторы, про исключения.
Там было больше двух слов.
Doc>Вот тут огромный LOL... посмешил. Doc>Если Adult дает гарантию возраста при передаче в метод, то передача Validated вообще ничего не гарантирует. Что помешает создать Validated который содержит юзера-мужчину и передать в метод, который ожидает что юзер будет женщиной? Тип тот же. Компиляция пройдет на ура. Так что у тебя в Validated может быть любая комбинация условий, что никакой гарантии уже не дает.
Он гарантирует, что тип проверен. Разумеется от дураков он не защищает. Если кто-то хочет создать тип, а потом обмануть его, то он сможет это сделать. Я не понял только, что мешает написать точно так же неверный контракт? Это ровно тоже самое.
Я тебе пытаюсь сказать, что можно создать тип которые защитит тебя, а ты в ответ говоришь, что можешь его сломать специально. Ну да можешь, ты вообще все можешь сломать. В этом цель?
Doc>Зато метод определяет свой контракт, а не какой-то внешний Validated в котором может быть все что угодно. А после завтра вообще придет твой коллега и перепишет Validated на свой лад.
Это не какой-то внешний Validated, это тип с которым готов работать метод. Тебя название смущает? Так это от твоей привычки пихать все в общие типы, который потом будет использоваться везде, чтобы не дай бог не создать лишний класс. А так-то это название просто для примера.
Doc>Где я сказал что это допустимо и более того что это правильно. Прочитай что процитировал сам же "у приложение уже ненормальное состояние". Это не
нормально, не допустимо и не правильно. Может быть из-за ошибки в коде или логики. Именно поэтому экстренное
Это прямо следует из твоего описания. Дизайн предполагает, что ошибки будут и с ними надо бороться. Еще раз — ДИЗАЙН в котором неверное состояние ОЖИДАЕМО.
Doc>Ну ты уже показал что ничего невозможного нет, заменив Adult на Validated :D
Ну если ты это можешь делать в бессознательном состоянии, то да. Хотят не понимаю, чем тут контракты помогут. У меня речь шла про непреднамеренные ошибки. Если ты мне хочешь доказать, что ты все можешь сломать, то признаю — тут любые методы бессильны. Ты победил.
Здравствуйте, Poopy Joe, Вы писали:
PJ>Ничего не понял. Причем тут совпадение параметров? Если у тебя контракт декларируется только в одном месте, то нафиг тогда они тебе вообще нужны?
При том что контракт метода это не одна строка. Это условия для всех параметров, это при необходимости еще и инварианты, это выходные значения. И вот сочетания всего этого редко повторяются от метода к методу.
PJ>Тут код размазанный про всему приложению, что еще хуже.
С чего он размазанный? Но находится ровно в своем месте.
PJ>Он гарантирует, что тип проверен. Разумеется от дураков он не защищает. Если кто-то хочет создать тип, а потом обмануть его, то он сможет это сделать. Я не понял только, что мешает написать точно так же неверный контракт? Это ровно тоже самое.
Ну юли, это не тоже самое. Вот ты передаёшь другим разработчикам библиотеку. Там есть 2 метода:
public void DoSomething1(..., int userAge)
{
...
Contract.Requires(_minAge < userAge);
...
}
public void DoSomething2(..., ValidatedUser user)
{
...
}
Если ты попробуешь не читая документации передать не валидный возраст в DoSomething1(), то получишь по рукам исключением (причем вполне конкретным).
Что сделает DoSomething2() если там будет не валидное состояние user — зависит исключительно от конкретной реализации. Может просто молча фигню сделает, или что выкинет ... неизвестно. Причем догадаться можно будет только или прочитав документацию или изучив исходники. И все твои рассказы что типизация тут мол спасет от ошибки тут не чем.
PJ>Я тебе пытаюсь сказать, что можно создать тип которые защитит тебя, а ты в ответ говоришь, что можешь его сломать специально. Ну да можешь, ты вообще все можешь сломать. В этом цель?
Как раз не специально. По ошибке. Не прочитал документацию, допустил ошибку где-то в коде и загрузил в user не то... Но типы то сошлись Твой метод работает только если за конкретное условие будет отвечать конкретный класс.
PJ>Это не какой-то внешний Validated, это тип с которым готов работать метод.
Это просто два жестко связанных класса. И надо помнить, что их реализации связанны. Шикарно.
PJ>Это прямо следует из твоего описания. Дизайн предполагает, что ошибки будут и с ними надо бороться. Еще раз — ДИЗАЙН в котором неверное состояние ОЖИДАЕМО.
Ага, тогда любой код с try {} catch () это такой дизайн. А сэр не использует catch вообще? :D
А вообще нормально когда дизайн предполагает что могут пытаться некорректно использовать, что могут пытаться ломать (особенно там где безопасность важна).
Но как я понимаю для тебя — лучше неопределенное поведение DoSomething2() чем определенное DoSomething1().
Doc>>Ну ты уже показал что ничего невозможного нет, заменив Adult на Validated :D PJ>Ну если ты это можешь делать в бессознательном состоянии, то да.
Я тебе выше показал пример с библиотекой. Контракт вполне защищает от некорректного использования, а твой метод — нет. О чем еще говорить.
PJ>Хотят не понимаю, чем тут контракты помогут. У меня речь шла про непреднамеренные ошибки.
О них и речь. Пример опять же выше — другой программист использующий твой код. И ты предлагаешь довериться что другой программист все будет делать без ошибкой и карать его неопределенным поведением. По твоему получится это лучше предсказуемого контракта.
Здравствуйте, Doc, Вы писали:
Doc>При том что контракт метода это не одна строка. Это условия для всех параметров, это при необходимости еще и инварианты, это выходные значения. И вот сочетания всего этого редко повторяются от метода к методу.
И? Я про то и говорил, что кода с контрактами у тебя будет еще больше. О чем ты вообще?
Doc>С чего он размазанный? Но находится ровно в своем месте.
Месте где ему вообще не место. А если еще методу надо будет проверять возраст? В одном месте ставим контаракт на возраст в другом не ставим? Тут играем, тут рыбу заворачиваем? Клёво, ага. Вместо типа, который сам по себе надёжный контракт.
Doc>Ну юли, это не тоже самое. Вот ты передаёшь другим разработчикам библиотеку. Там есть 2 метода:
Doc>
Doc>Если ты попробуешь не читая документации передать не валидный возраст в DoSomething1(), то получишь по рукам исключением (причем вполне конкретным).
Doc>Что сделает DoSomething2() если там будет не валидное состояние user — зависит исключительно от конкретной реализации. Может просто молча фигню сделает, или что выкинет ... неизвестно. Причем догадаться можно будет только или прочитав документацию или изучив исходники. И все твои рассказы что типизация тут мол спасет от ошибки тут не чем.
Ты так и не ответил на вопрос каким образом в ValidatedUser окажется невалидное состояние. Уже третий раз спрашиваю.
Я понимаю, что это привычное состояние когда хлоп в float age какая-то хрень. А вот тут как оно так может случится?
Doc>Как раз не специально. По ошибке. Не прочитал документацию, допустил ошибку где-то в коде и загрузил в user не то... Но типы то сошлись Твой метод работает только если за конкретное условие будет отвечать конкретный класс.
Давай вернемся к простому Adult<User>. Изобрази случай когда по ошибке ты передаешь в метод невалидный Adult<User>. Продемонстрируй как по ошибке ты туда загрузишь "не то".
Doc>Это просто два жестко связанных класса. И надо помнить, что их реализации связанны. Шикарно.
Что значит помнить? Ну забудь, компилятор напомнит. А вот забыть, что там какой-то контракт проверяет чего-то можно запросто.
Doc>Ага, тогда любой код с try {} catch () это такой дизайн. А сэр не использует catch вообще? :D
Как элемент своего дизайна? Нет, разумеется. Строить поведение на исключениях это
Doc>А вообще нормально когда дизайн предполагает что могут пытаться некорректно использовать, что могут пытаться ломать (особенно там где безопасность важна).
Oк. void DoSomething2(Adult<User> user){...};
Сломай. Покажи уже на примере, как его можно некорректно использовать.
Doc>Но как я понимаю для тебя — лучше неопределенное поведение DoSomething2() чем определенное DoSomething1().
У меня-то как раз определенной поведение DoSomething2, а в отличии от твоего DoSomething1, который может сработать, а может и исключение выкинуть.
Doc>Я тебе выше показал пример с библиотекой. Контракт вполне защищает от некорректного использования, а твой метод — нет. О чем еще говорить.
Ничего ты не показал. Вдруг тип по волшебству стал содержать неверные данные это фантазия. А вот забыли обновить контракт — реальность.
Doc>О них и речь. Пример опять же выше — другой программист использующий твой код. И ты предлагаешь довериться что другой программист все будет делать без ошибкой и карать его неопределенным поведением. По твоему получится это лучше предсказуемого контракта.
Ровно наоборот. С контрактами надо лазать по чужому коду и смотреть какие там контракты и чего он проверяет. А если метод принимает Adult<Age>, то тут ошибиться просто невозможно. Validated тебе не нравится? Ну так не надо называть так. Неудачное имя использованное в контексте одного примера.
PJ>Нет. Вместо проверки значения типа в рантайме лучше использовать в параметрах тип в котором недопустимые значения просто невозможны.
Это не всегда возможно. Точнее, сделать можно, но по сложности оно не будет выигрывать у стандартных проверок.
это дело исчерпывающе обсуждалось, толкового решения емнип так и не привели.
Где-то в ветке приводили пример с Adult<Age>. Такие штуки работают до момента, пока все условия инвариантны, их мало и условия не зависят от контекста. Потом начинают вылезать грабли.
Самый простой пример: возраст совершеннолетия зависит от страны и не обязательно гарантирует все права (например, крепкое спиртное в Финляндии продаётся начиная с 20 лет, совершеннолетие — с 18). Попробуйте изобразить это с помощью типов
При этом по факту граница ещё более размыта. Получение определённых прав помимо возраста может зависеть от точности gps (Швейцария, в соседних кантонах разные правила продажи спиртного) или от вероисповедования (Пакистан, снова продажа спиртного). От согласия родителей/опекунов (несовершеннолетние). От места рождения и от судимости (право избираться президентом в отдельных странах). От места работы (неграждане в Латвии). Ну и так далее.
Я уж молчу про возраст как основание для получения пенсии — добавляем пол, календарную выслугу, тяжелые/вредные условия труда, состояние здоровья, получение пенсии в другой стране (при двойном гражданстве), дату рождения и тыды и тыпы.
В общем, лучше не пытаться натянуть типы туда, где они явно будут мешать.
Здравствуйте, Poopy Joe, Вы писали:
PJ>Я про то и говорил, что кода с контрактами у тебя будет еще больше. О чем ты вообще?
Больше только в том случае, если ты будешь игнорировать проверки.
Doc>>С чего он размазанный? Но находится ровно в своем месте. PJ>Месте где ему вообще не место. А если еще методу надо будет проверять возраст? В одном месте ставим контаракт на возраст в другом не ставим? Тут играем, тут рыбу заворачиваем? Клёво, ага. Вместо типа, который сам по себе надёжный контракт.
С чего это? Интерфейс (метод) диктует условия, условия в нем. Все логично.
PJ>Ты так и не ответил на вопрос каким образом в ValidatedUser окажется невалидное состояние. Уже третий раз спрашиваю. PJ>Я понимаю, что это привычное состояние когда хлоп в float age какая-то хрень. А вот тут как оно так может случится?
Серьезно не понимаешь? Печаль. Ты писал > Либо вообще просто изменю тип Validated<User> добавив в него какие угодно ограничения.
1) Создаем ValidatedUser у которого из ограничений, например, только вес < 100 кг. Остальные ограничения не заданы.
2) Записываем в него девушку, 60кг.
3) Предаем в DoSomething2(..., ValidatedUser user) который ждет только мужчин свыше 100кг.
Doc>>Как раз не специально. По ошибке. Не прочитал документацию, допустил ошибку где-то в коде и загрузил в user не то... Но типы то сошлись Твой метод работает только если за конкретное условие будет отвечать конкретный класс. PJ>Давай вернемся к простому Adult<User>. Изобрази случай когда по ошибке ты передаешь в метод невалидный Adult<User>. Продемонстрируй как по ошибке ты туда загрузишь "не то".
Ну ты совсем уж... Я же написал "Твой метод работает только если за конкретное условие будет отвечать конкретный класс." Как только набираются разные условия, то или плоди по классу на условие и создавай кучу кода ну или как ты предложил ValidatedUser и нафиг все проверки :D
Doc>>Это просто два жестко связанных класса. И надо помнить, что их реализации связанны. Шикарно. PJ>Что значит помнить? Ну забудь, компилятор напомнит. А вот забыть, что там какой-то контракт проверяет чего-то можно запросто.
Ты точно понимаешь о чем говоришь?
Компилятор напомнит только о том, что надо передать один класс в другой. А что помешает потом поменять логику в одном так, что все поломается? Подчеркну, не специально. Пришел новый разработчик, он уведёт из кода только класс с кучей проверок ValidatedUser передается в метод. Как он угадает какая проверка нужна именно для этого класса
А контракт ты можешь забыть, и сразу получишь по рукам. В некоторых случаях еще при компиляции. Да и просто, если открыть метод и увидеть
Contract.Requires(_minAge < age);
что тут не ясного?
Doc>>Ага, тогда любой код с try {} catch () это такой дизайн. А сэр не использует catch вообще? :D PJ>Как элемент своего дизайна? Нет, разумеется. Строить поведение на исключениях это
У тебя некрасивая манера общения — придумывать за оппонента. Поведение не строиться на исключениях. Исключения это как раз исключительная ситуация.
Кроме того, ты специально игнорируешь мои слова что после исключения приложение завершает работу.
Doc>>А вообще нормально когда дизайн предполагает что могут пытаться некорректно использовать, что могут пытаться ломать (особенно там где безопасность важна). PJ>Oк. void DoSomething2(Adult<User> user){...}; PJ>Сломай. Покажи уже на примере, как его можно некорректно использовать.
Как ты быстро вернулся в 1 условию. Уже расписал тебе выше.
Doc>>Но как я понимаю для тебя — лучше неопределенное поведение DoSomething2() чем определенное DoSomething1(). PJ>У меня-то как раз определенной поведение DoSomething2, а в отличии от твоего DoSomething1, который может сработать, а может и исключение выкинуть.
С чего оно определённое? Ты ждал одно, получил другое. Пример выше.
Doc>>Я тебе выше показал пример с библиотекой. Контракт вполне защищает от некорректного использования, а твой метод — нет. О чем еще говорить. PJ>Ничего ты не показал. Вдруг тип по волшебству стал содержать неверные данные это фантазия. А вот забыли обновить контракт — реальность.
Забыли обновить контракт? Обновление контракта вообще исключительная ситуация. Ты точно путаешь BL и контракты.
Предлагаю так, напиши кода для следующего
1) Есть тип User который содержит кучу инфы о юзере.
2) Напиши 3 метода (только сигнатуры и необходмый для пояснения код)
— IsAdult проверяет <= 18 лет
— DoSomething1 — ожидает что ему передадут email и возраст пользователя. По условиям BL вызывается только для пользователей <= 18
— DoSomething2 — ожидает мужчин младше 18
— DoSomething3 — ожидает женщин старше 18 и блондинки
— DoSomething4 — ожидает женщин старше 18 и вес которых больше 60 кг
— DoSomething5 — ожидает женщин старше 18 и младше 40
Вот и поглядим как выглядит твой подход. Я тоже самое на контрактах.
Согласен?
Здравствуйте, Doc, Вы писали: Doc>Больше только в том случае, если ты будешь игнорировать проверки.
У меня нет игнорируемых проверок. Все что не проверяется явно проверяется неявно компилятором. Doc>С чего это? Интерфейс (метод) диктует условия, условия в нем. Все логично.
С того что это нарушает Single Responsibility Principle. Тут используем возраст, а тут его же и декларируем и проверем, и прочее. Doc>Серьезно не понимаешь? Печаль. Ты писал
Ты вообще не понял, что я писал. Ощущение, что кто-то из нас разговаривает сам с собой.
Метод принимает не сферический Validated<User>, а тот кторый его удовлетворяет. Да имя не удачно, было выбрано только чтобы показать, что нет привязки к одному полю. Но сколько это можно пережевывать-то? >> Либо вообще просто изменю тип Validated<User> добавив в него какие угодно ограничения. Doc>1) Создаем ValidatedUser у которого из ограничений, например, только вес < 100 кг. Остальные ограничения не заданы. Doc>2) Записываем в него девушку, 60кг. Doc>3) Предаем в DoSomething2(..., ValidatedUser user) который ждет только мужчин свыше 100кг.
Это ровно обратное тому, что я объяснял. По моему из Adult<Age> все прекрасно понять, чтобы выдвать вот такое...
В первом же сообщении было написано, что ты может создать только валидный тип. Doc>Как только набираются разные условия, то или плоди по классу на условие и создавай кучу кода ну или как ты предложил ValidatedUser и нафиг все проверки :D
Ни то, ни другое. По классу на "контракт". Doc>Ты точно понимаешь о чем говоришь?
Хочешь уже скатить до уровня сам дурак? Doc>А что помешает потом поменять логику в одном так, что все поломается?
А что помешает написать неверный контракт? Да ничего. Doc>Пришел новый разработчик, он уведёт из кода только класс с кучей проверок ValidatedUser передается в метод. Как он угадает какая проверка нужна именно для этого класса
Ну если там экономили на типах, то наверно придется гадать. А в нормальном случае какой тип передается — такой и нужен. Doc>А контракт ты можешь забыть, и сразу получишь по рукам. В некоторых случаях еще при компиляции. Да и просто, если открыть метод и увидеть Doc>Contract.Requires(_minAge < age); Doc>что тут не ясного?
Неясно кто именно возмущался, что надо открывать чужой код.
Именно что в некоторых случаях. Т.е. вместо проверки типа, работающей всегда, — костыли которые работают в некоторых случаях. А в некоторых случаях можно получить исключение в райнтайме, потому что про некоторые случаи забыли. Чего ты сам-то ограничился одним полем? Напиши сюда их с десяток и сразу будет понятно как их можно забыть и пропустить. Это соврешненно обычное дело. Doc>У тебя некрасивая манера общения — придумывать за оппонента. Поведение не строиться на исключениях. Исключения это как раз исключительная ситуация.
Конечно строится. Ты заведомо планируешь, что программа может упасть, потому что в данных окажется мусор. Doc>Кроме того, ты специально игнорируешь мои слова что после исключения приложение завершает работу.
И? Это самый худший случай. О чем ты тут хочешь поговорить? Doc>Как ты быстро вернулся в 1 условию. Уже расписал тебе выше.
Ерунду ты расписал. Потому, что с двумя ты уже ничего не понял и сделал какие-то феерические выводы строго обратные тому, что я говорил. Doc>С чего оно определённое? Ты ждал одно, получил другое. Пример выше.
Неверный пример. Вернемся к Adult<Age>. Сломай его. Doc>Забыли обновить контракт? Обновление контракта вообще исключительная ситуация. Ты точно путаешь BL и контракты.
Да ну прям. Ты точно путаешь контракты и священные тексты. Контракты могут меняться при первом же рефакторинге. Doc>Предлагаю так, напиши кода для следующего Doc>1) Есть тип User который содержит кучу инфы о юзере. Doc>2) Напиши 3 метода (только сигнатуры и необходмый для пояснения код) Doc>- IsAdult проверяет <= 18 лет Doc>- DoSomething1 — ожидает что ему передадут email и возраст пользователя. По условиям BL вызывается только для пользователей <= 18 Doc>- DoSomething2 — ожидает мужчин младше 18 Doc>- DoSomething3 — ожидает женщин старше 18 и блондинки Doc>- DoSomething4 — ожидает женщин старше 18 и вес которых больше 60 кг Doc>- DoSomething5 — ожидает женщин старше 18 и младше 40 Doc>Вот и поглядим как выглядит твой подход. Я тоже самое на контрактах. Doc>Согласен?
Пиши
class IDCard
{
public string Gender { get; set; }
public string HairColor { get; set; }
public string Weight { get; set; }
public string DateOfBirth { get; set; }
public string Email { get; set; }
}
class HairColor { }
class Weight { }
class Email { }
class Age : IComparable<Age>
{
public int CompareTo(Age other) {...}
}
struct Optional<T> /* Наивная реализация для примера */
{
public bool HasValue() {...}
public T Value { get; set; }
}
class Person : IComparable<Age>
{
public HairColor HairColor { get; set; }
public Weight Weight { get; set; }
public Age Age { get; set; }
public Email Email { get; set; }
public int CompareTo(Age other) {...}
}
class Male : Person { }
class Female : Person { }
class Adult<T> where T : IComparable<Age>
{
public T Value { get; private set; }
public static Optional<Adult<T>> Create(T arg){...}
}
class UnderAge<T> where T : IComparable<Age>
{
public T Value { get; private set; }
public static Optional<UnderAge<T>> Create(T arg) {...}
}
class Blondie { public Blondie(Adult<Female> woman) { } }
class PlumpWoman { public PlumpWoman(Adult<Female> woman) { } }
class SexyWoman { public SexyWoman(Adult<Female> woman) { } }
}
class Program
{
static bool SendEmail(Email email, UnderAge<Age> age) => true;
static bool PlayWith(UnderAge<Male> boy) => true;
static bool AskMath(Blondie woman) => true;
static bool AskRecipe(PlumpWoman woman) => true;
static bool FindWife(SexyWoman woman) => true;
static Optional<Person> Parser(IDCard card) => new Optional<Person>(/*...*/);
static void Main(string[] args)
{
var rawData = new IDCard[10];
var persons = rawData
.Select(Parser)
.Where(p => p.HasValue())
.Select(p => p.Value)
.ToArray();
var children = persons.Select(UnderAge<Person>.Create)
.Where(p => p.HasValue()).Select(p => p.Value)
.ToArray();
var q1 = children.Select(p => new { Email = p.Value.Email, Age = UnderAge<Age>.Create(p.Value.Age).Value })
.Select(item => SendEmail(item.Email, item.Age));
var q2 = children
.OfType<UnderAge<Male>>()
.Select(PlayWith)
.ToArray();
var women = persons.Select(Adult<Person>.Create)
.Where(p => p.HasValue())
.OfType<Adult<Female>>()
.Select();
var q3 = women.Select(Blondie.Create)
.Where(p => p.HasValue())
.Select(p => p.Value)
.Select(AskMath)
.ToArray();
/* Остальные методы очевидны из q3 */
}
}
Здравствуйте, Sinix, Вы писали:
PJ>>Нет. Вместо проверки значения типа в рантайме лучше использовать в параметрах тип в котором недопустимые значения просто невозможны. S>Это не всегда возможно. Точнее, сделать можно, но по сложности оно не будет выигрывать у стандартных проверок.
Это возможно и никаких проблем в этом нет. На c# оно будет более многословно, чем на нормальных языках, но никаких сложностей в сравнении "со стандартными проверками" нет. Если использовать нормальные языки с паттерн матчинг, то "стандартные проверки" стыдливо курят в сторонке.
S>Вот тут
это дело исчерпывающе обсуждалось, толкового решения емнип так и не привели.
Почитал немного. Очень смешное обсуждение. Все функциональные языки используют как раз типы. Заявлять, что "Высокоуровневую логику на типах писать сложно и обычно нерентабельно" это сильно
Потом почитаю почему там "ничего не получилось". Скорее всего не хотели или как Doc экономили на типах.
S>Где-то в ветке приводили пример с Adult<Age>. Такие штуки работают до момента, пока все условия инвариантны, их мало и условия не зависят от контекста. Потом начинают вылезать грабли.
Если они начали вылезать, то дизайн уже кривой и его надо пересматривать до момента когда грабли исчезнут. Здорово, правда?
S>Самый простой пример: возраст совершеннолетия зависит от страны и не обязательно гарантирует все права (например, крепкое спиртное в Финляндии продаётся начиная с 20 лет, совершеннолетие — с 18). Попробуйте изобразить это с помощью типов
Я собственно это и предложил, в этой же ветке. В чем проблема изобразить это в типах?
S>При этом по факту граница ещё более размыта. Получение определённых прав помимо возраста может зависеть от точности gps (Швейцария, в соседних кантонах разные правила продажи спиртного) или от вероисповедования (Пакистан, снова продажа спиртного). От согласия родителей/опекунов (несовершеннолетние). От места рождения и от судимости (право избираться президентом в отдельных странах). От места работы (неграждане в Латвии). Ну и так далее.
И? А контракты тут каким местом тут чего изменят?
S>Я уж молчу про возраст как основание для получения пенсии — добавляем пол, календарную выслугу, тяжелые/вредные условия труда, состояние здоровья, получение пенсии в другой стране (при двойном гражданстве), дату рождения и тыды и тыпы.
Не молчи, расскажи.
Здравствуйте, Poopy Joe, Вы писали:
PJ>Я собственно это и предложил, в этой же ветке. В чем проблема изобразить это в типах?
Кэп: в том, что большинство проверок имеет смысл внутри одного-двух конкретных методов, а данные надо протаскивать по всему коду. Т.е. на вход тебе приходит person, и твой каст к Adult<Person> с костылями в виде HasValue() от стандартңого BL.AssertIsAdult(person) отличается только количеством приседаний на ровном месте.
S>>При этом по факту граница ещё более размыта. Получение определённых прав помимо возраста может зависеть от точности gps (Швейцария, в соседних кантонах разные правила продажи спиртного) или от вероисповедования (Пакистан, снова продажа спиртного). От согласия родителей/опекунов (несовершеннолетние). От места рождения и от судимости (право избираться президентом в отдельных странах). От места работы (неграждане в Латвии). Ну и так далее. PJ>И? А контракты тут каким местом тут чего изменят?
Кэп #2: будут гарантировать соблюдение всех предусловий значительно меньшими силами? Точнее даже не будут, а гарантируют, т.к. я всю эту радость на практике использую, а не теоретизирую
S>>Я уж молчу про возраст как основание для получения пенсии — добавляем пол, календарную выслугу, тяжелые/вредные условия труда, состояние здоровья, получение пенсии в другой стране (при двойном гражданстве), дату рождения и тыды и тыпы. PJ>Не молчи, расскажи.
Открываем 400-ФЗ и читаем :P
Я из него самые простые моменты выбрал.
И да, автоматизация расчёта подобных штук _очень_ нуждается в проверке кода всеми возможными средствами, от тестов и до ассертов/контрактов. Если подход с типами на таких масштабах сливается — имеет ли смысл его использовать на чём-то меньшем?
P.S. у меня ещё пара похожих вещей в загашнике есть, можно начать с разбора PSD, о котором лучше всего говорит вот этот комментарий. А потом перейти к pdf-у
Здравствуйте, Poopy Joe, Вы писали:
Doc>>С чего это? Интерфейс (метод) диктует условия, условия в нем. Все логично. PJ>С того что это нарушает Single Responsibility Principle. Тут используем возраст, а тут его же и декларируем и проверем, и прочее.
А с чего ты взял что это разные ответственности?
PJ>Метод принимает не сферический Validated<User>, а тот кторый его удовлетворяет. Да имя не удачно, было выбрано только чтобы показать, что нет привязки к одному полю. Но сколько это можно пережевывать-то?
А кто говорил: >>> Либо вообще просто изменю тип Validated<User> добавив в него какие угодно ограничения.
Ты определись тогда — или Validated с кучей проверок или по 1 классу на каждый набор условий.
Doc>>Как только набираются разные условия, то или плоди по классу на условие и создавай кучу кода ну или как ты предложил ValidatedUser и нафиг все проверки :D PJ>Ни то, ни другое. По классу на "контракт".
А, все же каждая проверка — отдельный класс. Все тогда все понятно что это даст кучу дополнительного кода на ровном месте.
Doc>>А что помешает потом поменять логику в одном так, что все поломается? PJ>А что помешает написать неверный контракт? Да ничего.
Еще раз — контракт находится при методе для которого он нужен. Твой класс c проверкой — связан как я понимаю с несколькими другими классами.
Doc>>У тебя некрасивая манера общения — придумывать за оппонента. Поведение не строиться на исключениях. Исключения это как раз исключительная ситуация. PJ>Конечно строится. Ты заведомо планируешь, что программа может упасть, потому что в данных окажется мусор.
Не путай поведение по BL с обработкой исключительный ситуаций.
Doc>>Кроме того, ты специально игнорируешь мои слова что после исключения приложение завершает работу. PJ>И? Это самый худший случай. О чем ты тут хочешь поговорить?
Опять вырываешь из контекста.
Doc>>Забыли обновить контракт? Обновление контракта вообще исключительная ситуация. Ты точно путаешь BL и контракты. PJ>Да ну прям. Ты точно путаешь контракты и священные тексты. Контракты могут меняться при первом же рефакторинге.
Нет не путаю. Хотя верю что, учитывая число классов и кучу взаимосвязей — рефакторинг у тебя дело наверное еженежельное :D
Doc>>Вот и поглядим как выглядит твой подход. Я тоже самое на контрактах. Doc>>Согласен?
PJ>Пиши
Ты называешь linq запрос методом? Я просил показать класс с 5 методами, которые ожидают соответствующие параметры.
Ну хоть одно из твоего кода ясно, у тебя каждая проверка — отдельный класс. Т.к. как я и говорил до этого, у тебя будут что-то вроде FemaleWithMinHeightAndMaxWeightAndBlondHair
Что породит кучу кода как я и говорил.
Здравствуйте, Doc, Вы писали:
Doc>А с чего ты взял что это разные ответственности?
С того, что контракт выставляют на внешние сущности.
Doc>А кто говорил: >>>> Либо вообще просто изменю тип Validated<User> добавив в него какие угодно ограничения.
ДЛЯ ДАННОГО МЕТОДА. У тебя похоже аргументов, кроме как прицепится к словам не осталось.
Doc>Ты определись тогда — или Validated с кучей проверок или по 1 классу на каждый набор условий.
У меня это в явном виде написано буквально парой строк ниже.
Doc>А, все же каждая проверка — отдельный класс. Все тогда все понятно что это даст кучу дополнительного кода на ровном месте.
А, ну понятно. Экономим классы, но тратим время на отладку. Ну, удачи.
Doc>Еще раз — контракт находится при методе для которого он нужен. Твой класс c проверкой — связан как я понимаю с несколькими другими классами.
Контракт находится в месте где происходит потребление, только и всего. Это самое последнее место, дальше уже просто некуда откладывать. Это самое неудачное место куда его можно поставить, так как уже слишком поздно пить боржоми.
Doc>Не путай поведение по BL с обработкой исключительный ситуаций.
Если у тебя прошел в данных мусор, то вот такая у тебя BL.
Doc>Опять вырываешь из контекста.
Где? В твое контексте уронить программу это нормально.
Doc>Нет не путаю. Хотя верю что, учитывая число классов и кучу взаимосвязей — рефакторинг у тебя дело наверное еженежельное :D
Путаешь. Не, ну их конечно можно не менять, но тогда они просто вообще будет работать хаотически.
Конечно еженедельное, я могу менять код как хочу, так как компилятор мой помощник. А вот с контрактами код будет страшно трогать, чтобы все не посыпалось.
Doc>Ты называешь linq запрос методом? Я просил показать класс с 5 методами, которые ожидают соответствующие параметры.
У тебя проблемы с чтением кода?
Doc>Ну хоть одно из твоего кода ясно, у тебя каждая проверка — отдельный класс. Т.к. как я и говорил до этого, у тебя будут что-то вроде FemaleWithMinHeightAndMaxWeightAndBlondHair Doc>Что породит кучу кода как я и говорил.
Пока ясно, что ты ничего не написал, с чем можно сравнивать, ни привел ни одного корректного возражения.
S>Кэп: в том, что большинство проверок имеет смысл внутри одного-двух конкретных методов, а данные надо протаскивать по всему коду.
Твое утверждение совершенно высосано из пальца.
S>Т.е. на вход тебе приходит person, и твой каст к Adult<Person> с костылями в виде HasValue() от стандартңого BL.AssertIsAdult(person) отличается только количеством приседаний на ровном месте.
S>>>При этом по факту граница ещё более размыта. Получение определённых прав помимо возраста может зависеть от точности gps (Швейцария, в соседних кантонах разные правила продажи S>Кэп #2: будут гарантировать соблюдение всех предусловий значительно меньшими силами? Точнее даже не будут, а гарантируют, т.к. я всю эту радость на практике использую, а не теоретизирую
Не будет. Контракты вообще ничего не гарантируют, кроме того, что могут свалится в самом неожиданном месте. Ты тут пытаешься авторитетом задавить? Я тоже пробовал использовать, по результатам опыта — выкинул нафиг, как бесполезную ерунду.
S>Открываем 400-ФЗ и читаем :P
Т.е. контракты закончили и перешли к бухгалтерии? Что это вообще было?
S>И да, автоматизация расчёта подобных штук _очень_ нуждается в проверке кода всеми возможными средствами, от тестов и до ассертов/контрактов. Если подход с типами на таких масштабах сливается — имеет ли смысл его использовать на чём-то меньшем?
Ты сейчас возражаешь своему внутреннему голосу, потому что не озвучил проблему.
Здравствуйте, Poopy Joe, Вы писали:
Doc>>А с чего ты взял что это разные ответственности? PJ>С того, что контракт выставляют на внешние сущности.
Ну-ну. Контракт выставляется на параметры метода, а не на сущность.
Т.е. внутри метода выставление его же требований это чужая ответственность?
Просто для интереса — ты когда контракт на работу кто-то заключает, то выставление требований это его дело или обязанность кто-то со стороны. :D
PJ>ДЛЯ ДАННОГО МЕТОДА. У тебя похоже аргументов, кроме как прицепится к словам не осталось.
Как ты объясняешь, так и говорю. Тебе был вопрос "будешь писать по классу на каждое условие", ты сказал "напишу ValidatedUser с любыми условиями".
Doc>>А, все же каждая проверка — отдельный класс. Все тогда все понятно что это даст кучу дополнительного кода на ровном месте. PJ>А, ну понятно. Экономим классы, но тратим время на отладку. Ну, удачи.
С чего? То что делают куча твоих классов прекрасно делают контракты в несколько строк.
PJ>Контракт находится в месте где происходит потребление, только и всего. Это самое последнее место, дальше уже просто некуда откладывать. Это самое неудачное место куда его можно поставить, так как уже слишком поздно пить боржоми.
Еще раз "нарушение контракта — исключительная ситуация". Ее не должно быть в принципе. Это не средство управления, а средство как раз отладки и контроля.
Т.е. если у нас метод для логина пользователей только с подтверждённым email, то проверка наличия подтвержденного email это просто проверка, а не контракт.
Doc>>Не путай поведение по BL с обработкой исключительный ситуаций. PJ>Если у тебя прошел в данных мусор, то вот такая у тебя BL.
Ну я так понимаю сэр пишет вообще без багов. И его код используют разработчики которые пишут тоже всегда без багов. Ну только удивиться остается. :D
Doc>>Опять вырываешь из контекста. PJ>Где? В твое контексте уронить программу это нормально.
Это не нормально.
PJ>Конечно еженедельное, я могу менять код как хочу, так как компилятор мой помощник. А вот с контрактами код будет страшно трогать, чтобы все не посыпалось.
Еженедельный рефакторниг? :D Да, высокое качество кода без единого бага :D:D:D:D:D
Doc>>Ты называешь linq запрос методом? Я просил показать класс с 5 методами, которые ожидают соответствующие параметры. PJ>У тебя проблемы с чтением кода?
Твоего — видимо да. Покажи. Не вызовы, а твои методы.
PJ>Пока ясно, что ты ничего не написал, с чем можно сравнивать, ни привел ни одного корректного возражения.
Как только покажешь где у тебя те самые 5 твоих методов легко. Пока сравнивать не с чем.
Здравствуйте, Doc, Вы писали:
Doc>Ну-ну. Контракт выставляется на параметры метода, а не на сущность. Doc>Т.е. внутри метода выставление его же требований это чужая ответственность?
Он выставляется на те сущности которые не контролирует сам. В этом весь смысл.
Doc>Как ты объясняешь, так и говорю. Тебе был вопрос "будешь писать по классу на каждое условие", ты сказал "напишу ValidatedUser с любыми условиями".
Даже если было недопонимание, то я разжевал уже 10 раз, но ты продолжаешь свое. Видимо по делу возразить нечего.
Doc>С чего? То что делают куча твоих классов прекрасно делают контракты в несколько строк.
То-то ты их стесняешься привести. А такой был размах...
Doc>Еще раз "нарушение контракта — исключительная ситуация". Ее не должно быть в принципе. Это не средство управления, а средство как раз отладки и контроля. Doc>Т.е. если у нас метод для логина пользователей только с подтверждённым email, то проверка наличия подтвержденного email это просто проверка, а не контракт.
Если ее не должно быть в принципе, то зачем контракт? У меня вот ее не должно быть в принципе и ее в принципе и не будет.
Doc>Ну я так понимаю сэр пишет вообще без багов. И его код используют разработчики которые пишут тоже всегда без багов. Ну только удивиться остается. :D
Я так понимаю сэр перешел на передергивания?
Doc>Это не нормально.
У тебя нормально. Контракт ровно это и делает — роняет программу.
Doc>Еженедельный рефакторниг? :D Да, высокое качество кода без единого бага :D:D:D:D:D
Да хоть ежедневный. Я не боюсь менять код, ничего не развалится, как с контрактами.
Doc>Как только покажешь где у тебя те самые 5 твоих методов легко. Пока сравнивать не с чем.
Если ты не можешь читать код, то о чем вообще разговор? И это даже не код, а просто сигнатуры.
Здравствуйте, Poopy Joe, Вы писали:
S>>Кэп: в том, что большинство проверок имеет смысл внутри одного-двух конкретных методов, а данные надо протаскивать по всему коду. PJ>Твое утверждение совершенно высосано из пальца.
С чего бы это?
Возьмём самую простейшую штуковину: вендинговый автомат. Точнее сеть автоматов. С пивом-орешками-сигаретами-ещё чем.
На каждый из товаров есть свои ограничения по продаже, для простоты снова ограничимся пивом.
Опять-таки для полного счастья считаем, что все предусловия: возраст покупателя + местные законы + время суток известны. На входе у тебя Order с списком товаров + Customer + время-координаты от gps (автоматы часто возят по выставкам из штата в штат). На выходе — простой флаг: можно ли выдать заказ покупателю.
И, допустим, в одном кантоне пиво можно продать любому оболтусу, _но_ если автомат находится не в радиусе 500 м от школы/детского сада.
В другом — с 16 лет _или_ при наличии родителей (предлагает провести вторую карту), в третьем — только с 10 до 22-00 и только совершеннолетним. В четвёртых, часть марок предлагается только при заказе в комплекте с двумя пачками жвачки (одна из сетей проводит рекламную кампанию), в пятых — есть ещё и безалкогольное пиво, которое должно обрабатываться как пиво, ибо в зависимости от места продажи является подакцизным товаром.
Вперёд, можешь сам прикинуть трудоёмкость представления этой радости в виде системы типов. Напомню, проверяется на валидность не отдельный товар, а заказ в целом, т.к. промежуточное состояние вполне может быть невалидным.
С учётом того, что групп товаров — сотни (разные сети загружают разные товары), правила меняются динамически (не будешь же из-за очередной кампании перепрошивать все девайсы), а ответственность из-за нарушения правил торговли очень существенна и из-за штрафов и из-за репутации (потому что конкуренты тоже не спят).
А теперь учитываем, что помимо самих автоматов есть склады, логистика, интеграция с ПО поставщиков и тд и тп и вся эта радость по идее должна работать с одними и теми же данными.
Вперёд, попробуй распространить "проверенные" типы на всю систему
Можешь поверить, это далеко не самый сложный из возможных сценариев.
Здравствуйте, Poopy Joe, Вы писали:
PJ>Здравствуйте, Doc, Вы писали:
Doc>>Ну-ну. Контракт выставляется на параметры метода, а не на сущность. Doc>>Т.е. внутри метода выставление его же требований это чужая ответственность? PJ>Он выставляется на те сущности которые не контролирует сам. В этом весь смысл.
Во-первых, параметры метода не всегда сущности.
Во-вторых, контролировать сами сущности — не задача метода. Ему лишь надо данные в определенном состоянии.
Doc>>Ну я так понимаю сэр пишет вообще без багов. И его код используют разработчики которые пишут тоже всегда без багов. Ну только удивиться остается. :D PJ>Я так понимаю сэр перешел на передергивания?
Ни сколько. Ты же предполагаешь что нигде нет ошибок, все работает идеально, что в BL попасть что-то не то в принципе не может (багов то нет).
Doc>>Это не нормально. PJ>У тебя нормально. Контракт ровно это и делает — роняет программу.
Не надоело говорить за меня? Цитируешь одно и тут же утверждаешь обратное.
Doc>>Еженедельный рефакторниг? :D Да, высокое качество кода без единого бага :D:D:D:D:D PJ>Да хоть ежедневный. Я не боюсь менять код, ничего не развалится, как с контрактами.
Если тебе нужен постоянный рефакторниг это говорит о качестве твоего проектирования и кода. И боишься ли ты менять код или нет — тут не причем.
Думаю тут дальше говорить бессмысленно.
Doc>>Как только покажешь где у тебя те самые 5 твоих методов легко. Пока сравнивать не с чем. PJ>Если ты не можешь читать код, то о чем вообще разговор? И это даже не код, а просто сигнатуры.
В общем ты или прикидываешься или ... Я просил показать класс с 5 методами, которые ожидают данные. Ну хотя твой код показал главное — у тебя классы на каждый чих.
Вот тебе пример с контрактами. Обрати внимание, что IsAdult не требует контрактов.
Здравствуйте, Sinix, Вы писали:
S>>>Кэп: в том, что большинство проверок имеет смысл внутри одного-двух конкретных методов, а данные надо протаскивать по всему коду. PJ>>Твое утверждение совершенно высосано из пальца. S>С чего бы это?
С того, что ты взял с потолка утверждение и выдал его за какую-то общеизвестную истину.
S>Возьмём самую простейшую штуковину: вендинговый автомат. Точнее сеть автоматов. С пивом-орешками-сигаретами-ещё чем.
...
S>Опять-таки для полного счастья считаем, что все предусловия: возраст покупателя + местные законы + время суток известны. На входе у тебя Order с списком товаров + Customer + время-координаты от gps (автоматы часто возят по выставкам из штата в штат). На выходе — простой флаг: можно ли выдать заказ покупателю.
Э не, вот сейчас ты пытаешься натянуть сову на глобус и как-бы-ты-делал-на-проверках на систему типов. Мы это отвергаем.
У нас есть тип Order, вот он либо получится, либо нет. Никаких флагов и прочего бреда. В остальном задача прекрасно ложится на композицию типов. По мне так решать ее через проверки и ассерты это будет ад и израиль.
S>Вперёд, можешь сам прикинуть трудоёмкость представления этой радости в виде системы типов. Напомню, проверяется на валидность не отдельный товар, а заказ в целом, т.к. промежуточное состояние вполне может быть невалидным.
Да вообще никаких проблем. Каждое отдельное условие это один тип, что совершенно естественно, так как gps должен заниматься оделенный тип, как бы ты там не писал. Каждый тип имеет функции композиции с зависимыми типами. Если все зависимые типы сочетаются друг с другом на выходе тип Order, если нет, то ошибка, если чего-то не хватает то каррированый объект ожидающий недостающий тип. И то, что ты пытался запугать количеством вариантов тут перестает иметь значение, потому что количество условий для одного типа ограниченно и поддерживается довольно легко.
S>С учётом того, что групп товаров — сотни (разные сети загружают разные товары), правила меняются динамически (не будешь же из-за очередной кампании перепрошивать все девайсы), а ответственность из-за нарушения правил торговли очень существенна и из-за штрафов и из-за репутации (потому что конкуренты тоже не спят).
Именно поэтому я удивлен, что кто-то предлагает использовать ассерты. Этож свихнуться можно такое поддерживать.
S>Можешь поверить, это далеко не самый сложный из возможных сценариев.
И? Он разве выглядит страшным?
Здравствуйте, Doc, Вы писали:
Doc>Во-первых, параметры метода не всегда сущности.
А что это?
Doc>Во-вторых, контролировать сами сущности — не задача метода. Ему лишь надо данные в определенном состоянии.
возраст это не состояние, это именно контроль параметров.
Doc>Ни сколько. Ты же предполагаешь что нигде нет ошибок, все работает идеально, что в BL попасть что-то не то в принципе не может (багов то нет).
Я тебе предложил сломать и передать в метод мусор. Вместо решения ты начал разводить демагогию.
Doc>Если тебе нужен постоянный рефакторниг это говорит о качестве твоего проектирования и кода. И боишься ли ты менять код или нет — тут не причем.
При чем. Если ты боишься менять код, то это говорит о качестве твоего кода, а не то, что он у тебя написан идеально или требования не меняются. Остальное отмазки.
Doc>В общем ты или прикидываешься или ... Я просил показать класс с 5 методами, которые ожидают данные. Ну хотя твой код показал главное — у тебя классы на каждый чих.
Это ты прикидываешься. Я тебе дал полное решение, а ты половину. Ты приведи как ты их будешь использовать, сигнатуры с контрактами я могу представить и без тебя.
Doc>Вот тебе пример с контрактами. Обрати внимание, что IsAdult не требует контрактов.
Это просто отличный пример убогости контрактов.
Doc>
Doc> public class SomeClass
Doc> {
Doc> private readonly int _minAdultAge = 18;
Doc> private readonly int _maxRequiredAge = 40;
Doc> private readonly float _minWeight = 60.0f;
Doc> public bool IsAdult(int age) => _minAdultAge <= age;
Doc> public void DoSomething1(string email, int age)
Doc> {
Doc> Contract.Requires(!string.IsNullOrEmpty(email)); <--- пишем любую чушь, сгодится за email
Doc> Contract.Requires(_minAdultAge < age); <-- _minAge = -220, age = 9 -> контракт даже не пискнет. Возраст как у баобаба? Никаких проблем.
Doc> // ...
Doc> }
Doc> public void DoSomething2(User user)
Doc> {
Doc> Contract.Requires(user != null);
Doc> Contract.Requires(user.Sex == Sex.Male);
Doc> Contract.Requires(user.Age < _minAdultAge); <--- Age может быть меньше ноля
Doc> // ...
Doc> }
Здравствуйте, Poopy Joe, Вы писали:
S>>С чего бы это? PJ>С того, что ты взял с потолка утверждение и выдал его за какую-то общеизвестную истину.
Ну так это трюизм.
Подавляющее большинство контрактов имеет отношение только к конкретному методу.
Например, путь к файлу == null недопустим для сохранения документа, но обязателен для только что созданного. Для всего остального кода это ограничение не несёт никакого смысла.
Зато у них есть другие ограничения, которые имеют смысл тоже в ограниченном контексте.
Создавать по типу на каждый подобный метод — это полная и очевидная дурь. Пытаться выразить через систему типов все возможные состояния — дурь ещё большая из-за комбинаторного взрыва. Добавляем сюда неизбежную динамическую типизацию (спрятанную за OfType()/optional) и получаем те же ассерты, только в максимально извращённом виде.
Я серьёзно не вижу, как такой подход может масштабироваться дальше одного метода. Только не надо ссылаться на код из примера с возрастом — он вообще-то работать не будет.
var children = persons.Select(UnderAge<Person>.Create)
.Where(p => p.HasValue()).Select(p => p.Value)
.ToArray();
var q2 = children
.OfType<UnderAge<Male>>()
.Select(PlayWith)
.ToArray();
UnderAge<Person> не кастится к UnderAge<Male>.
PJ>У нас есть тип Order, вот он либо получится, либо нет. Никаких флагов и прочего бреда. В остальном задача прекрасно ложится на композицию типов. По мне так решать ее через проверки и ассерты это будет ад и израиль.
В том-то и проблема, что этот "валидный ордер" валидный в контексте ровно одной операции — получение заказа. Для оплаты, заказа предварительной доставки или оформления подарка тот же самый ордер может быть валиден. Что, будем городить свой тип для каждой операции?
Здравствуйте, Sinix, Вы писали:
S>Здравствуйте, Poopy Joe, Вы писали:
S>Ну так это трюизм.
Не, всего лишь высосанное из пальца утверждение.
S>Подавляющее большинство контрактов имеет отношение только к конкретному методу. S>Например, путь к файлу == null недопустим для сохранения документа, но обязателен для только что созданного. Для всего остального кода это ограничение не несёт никакого смысла. S>Зато у них есть другие ограничения, которые имеют смысл тоже в ограниченном контексте.
Ты хоть пример давай. А то, в очередной раз, выдаешь какое-то утверждение с которым никто и не спорил. Ну не имеет, и что?
S>Создавать по типу на каждый подобный метод — это полная и очевидная дурь. Пытаться выразить через систему типов все возможные состояния — дурь ещё большая из-за комбинаторного взрыва. Добавляем сюда неизбежную динамическую типизацию (спрятанную за OfType()/optional) и получаем те же ассерты, только в максимально извращённом виде.
По какому такому типу? Типу Nullable? Ага, так не нужен, что аж в во фреймворк ввели. Хотя лучше бы ввели Optional и Either. Лучи поноса им за это.
Никакой динамической типизации в примере нет.
S>Я серьёзно не вижу, как такой подход может масштабироваться дальше одного метода. Только не надо ссылаться на код из примера с возрастом — он вообще-то работать не будет.
Этот подход прекрасно работает в функциональных языках, на нем практически все и построено. Ты серьезно не видишь как работают функциональные языки? Ну, это, при желании, поправимо и точно не проблема подхода.
S>UnderAge<Person> не кастится к UnderAge<Male>.
Кастится, если добавить оператор, либо сделать это в явном виде еще одним селектом. Вообще ни на что ни влияет. Что ты этим хотел сказать?
S>В том-то и проблема, что этот "валидный ордер" валидный в контексте ровно одной операции — получение заказа. Для оплаты, заказа предварительной доставки или оформления подарка тот же самый ордер может быть валиден. Что, будем городить свой тип для каждой операции?
Что значит "городить"? У тебя тоже фобия "лишнего типа"? А зачем "городить" классы вообще? Запихать все в один метод и наставить ассертов.
Здравствуйте, Poopy Joe, Вы писали:
Doc>>Во-первых, параметры метода не всегда сущности. PJ>А что это?
Значения, данные... в принципе называй как хочешь. Если методу из всего User нужны только, скажем, пол и возраст, то передавать весь User только вредит. Достаточно два значения.
Doc>>Ни сколько. Ты же предполагаешь что нигде нет ошибок, все работает идеально, что в BL попасть что-то не то в принципе не может (багов то нет). PJ>Я тебе предложил сломать и передать в метод мусор. Вместо решения ты начал разводить демагогию.
Угу, это все равно что предложить сломать Hello World. Попытка хорошая, но все равно не зачет.
Doc>>Если тебе нужен постоянный рефакторниг это говорит о качестве твоего проектирования и кода. И боишься ли ты менять код или нет — тут не причем. PJ>При чем. Если ты боишься менять код, то это говорит о качестве твоего кода, а не то, что он у тебя написан идеально или требования не меняются. Остальное отмазки.
Не уходи в сторону. Еженедельный рефакторинг это именно о качестве кода.
PJ>Это просто отличный пример убогости контрактов.
Нет, это твои примеры как раз показатель того, что до самих контрактов у тебя не вышло доколупаться. Кстати, мой код хотя бы скомпилируется
Doc>> Contract.Requires(!string.IsNullOrEmpty(email)); <--- пишем любую чушь, сгодится за email
Придраться к контрактам не вышло, и ты пристал к методу IsNullOrEmpty. Нет проблем — напиши IsValidEmail и поставь тут.
Задача была показать пример контрактов.
Doc>> Contract.Requires(_minAdultAge < age); <-- _minAge = -220, age = 9 -> контракт даже не пискнет. Возраст как у баобаба? Никаких проблем.
Ну да, а если сделать класс, то проверка на min сама волшебным образом появится.
Ты вообще в своем примере не показал проверки.
PJ>Ну и так далее.
В общем претензий к контрактам у тебя нет. Ты просто доколупался до проверок в примере.
Здравствуйте, Doc, Вы писали:
Doc>В общем ты или прикидываешься или ... Я просил показать класс с 5 методами, которые ожидают данные. Ну хотя твой код показал главное — у тебя классы на каждый чих. Doc>Вот тебе пример с контрактами. Обрати внимание, что IsAdult не требует контрактов.
Безотносительно всей дискуссии. Мне так кажется, что на IsAdult точно должен быть контракт. Потому как предусловие есть — возраст не может/не должен быть меньше 0 — это явно неправильные данные и метод невалидно с ними работает.
Здравствуйте, Andir, Вы писали:
A>Безотносительно всей дискуссии. Мне так кажется, что на IsAdult точно должен быть контракт. Потому как предусловие есть — возраст не может/не должен быть меньше 0 — это явно неправильные данные и метод невалидно с ними работает.
Ну я подразумевал что вернется bool (т.е. true — взрослый). Как я сталкивался, то обычно так себя ведут методы и свойства Is<Что-то>, возвращая true если условие "что-то" выполняется.
Здравствуйте, Doc, Вы писали:
Doc>Здравствуйте, Poopy Joe, Вы писали:
Doc>Значения, данные... в принципе называй как хочешь. Если методу из всего User нужны только, скажем, пол и возраст, то передавать весь User только вредит. Достаточно два значения.
Хм... И пол, и возраст это сущности. Я зачем ты сказал остальное я так и не понял.
Doc>Угу, это все равно что предложить сломать Hello World. Попытка хорошая, но все равно не зачет.
Ну у тебя и того меньше, одна я показал где у тебя могут быть ошибки.
Doc>Не уходи в сторону. Еженедельный рефакторинг это именно о качестве кода.
Нет, не говорит. А вот его боязнь говорит. Может голосование запустить.
Doc>Нет, это твои примеры как раз показатель того, что до самих контрактов у тебя не вышло доколупаться. Кстати, мой код хотя бы скомпилируется
Вот именно этот факт и говорит об убогости контрактов.
Если у меня ошибка, то я просто не могу скомпилироваться, если у тебя ошибка может случится все что угодно.
Doc>Придраться к контрактам не вышло, и ты пристал к методу IsNullOrEmpty. Нет проблем — напиши IsValidEmail и поставь тут.
Чо? Все обсуждение вокруг этого и идет. Что у тебя проверки хаотические и размазаны по всему коду, а их валидность зачастую сомнительна. Может ты о чем-то своем говорил, но лично я вот о таком.
Doc>Задача была показать пример контрактов.
С их использованием. Одно без другого совершенно нелепо и ничего не показывает. Так что закончи.
Doc>Ну да, а если сделать класс, то проверка на min сама волшебным образом появится. Doc>Ты вообще в своем примере не показал проверки.
Потому что у меня есть тип, и делается все там. У тебя все делается в разных местах, никак не связанных.
Написать вторую половину коды ты вообще не осилил, а так хаоса добавится еще больше.
Doc>В общем претензий к контрактам у тебя нет. Ты просто доколупался до проверок в примере.
Есть и я их указал, если ты не понял в чем суть проблемы, то о чем мы вообще тогда?
Здравствуйте, Poopy Joe, Вы писали:
Doc>>Не уходи в сторону. Еженедельный рефакторинг это именно о качестве кода. PJ>Нет, не говорит. А вот его боязнь говорит. Может голосование запустить.
Ну хочешь поспорить сам с собой — вперед. Я не боюсь, мне, как видно в отличии от тебя, не надо рефакторить еженедельно.
Хотя, можешь узнать, что говорит о коде такой частый рефакторинг.
PJ>Если у меня ошибка, то я просто не могу скомпилироваться, если у тебя ошибка может случится все что угодно.
Ну да, нет у тебя проверок — нет ошибок.
Странно, как ты возможность скомпилировать код приравнял к тому, что в нем нет логических ошибок.
PJ>Потому что у меня есть тип, и делается все там. У тебя все делается в разных местах, никак не связанных.
Ну учитывая как ты придирался к моему коду, то в твоем нет проверок. И у тебя он не компилится и не работает
Doc>>В общем претензий к контрактам у тебя нет. Ты просто доколупался до проверок в примере. PJ>Есть и я их указал, если ты не понял в чем суть проблемы, то о чем мы вообще тогда?
Это не проблемы контрактов. Не написать проверку на min можно и в твоем коде. Ну или покажи как твой пример сразу укажет на отсутствие такой проверки.
Если ты считаешь что это проблема именно контрактов, то о чем мы вообще тогда?
Здравствуйте, Doc, Вы писали:
A>>Безотносительно всей дискуссии. Мне так кажется, что на IsAdult точно должен быть контракт. Потому как предусловие есть — возраст не может/не должен быть меньше 0 — это явно неправильные данные и метод невалидно с ними работает.
Doc>Ну я подразумевал что вернется bool (т.е. true — взрослый). Как я сталкивался, то обычно так себя ведут методы и свойства Is<Что-то>, возвращая true если условие "что-то" выполняется.
Если кто-то передал туда отрицательные значения — значит в вызывающем коде, что-то уже не так. И именно для этого и нужен конракт, обнаружить нарушение пред-условия -> возраст не может быть отрицательным. От того что там вернётся false станет только хуже, вызывающий код продолжить работать как ни в чём не бывало и ошибка всплывёт гораздо позднее.
Здравствуйте, Andir, Вы писали:
A>Если кто-то передал туда отрицательные значения — значит в вызывающем коде, что-то уже не так. И именно для этого и нужен конракт, обнаружить нарушение пред-условия -> возраст не может быть отрицательным. От того что там вернётся false станет только хуже, вызывающий код продолжить работать как ни в чём не бывало и ошибка всплывёт гораздо позднее.
Хорошее замечание. Согласен. Можно так или передавать byte/uint
Здравствуйте, Doc, Вы писали:
Doc>Ну хочешь поспорить сам с собой — вперед. Я не боюсь, мне, как видно в отличии от тебя, не надо рефакторить еженедельно. Doc>Хотя, можешь узнать, что говорит о коде такой частый рефакторинг.
Я не говорил надо, я говорил могу. Можешь посмотреть в словаре разницу. Мне пока видно, что ты записался в свет истины, все ему видно...
Doc>Ну да, нет у тебя проверок — нет ошибок. Doc>Странно, как ты возможность скомпилировать код приравнял к тому, что в нем нет логических ошибок.
Странно, что ты за 100500 сообщений так и не понял предмета обсуждения.
Doc>Ну учитывая как ты придирался к моему коду, то в твоем нет проверок. И у тебя он не компилится и не работает
В моем коде есть ошибка поэтому он даже не компилится, в твоем ест ошибки, но он компилится и работает неверно. Это достижение.
Doc>Это не проблемы контрактов. Не написать проверку на min можно и в твоем коде. Ну или покажи как твой пример сразу укажет на отсутствие такой проверки. Doc>Если ты считаешь что это проблема именно контрактов, то о чем мы вообще тогда?
Это будет странно, если ты пишешь тип, и совершенно нормально, когда ты пишешь однотипные контракты в 100500 мест. Одно и то же условие проверятся в 3х местах, т.е. нарушаются все приличные практики. И ты еще этим гордишься...
В общем, корректного возражения как можно сломать тип ты предъявить не смог. Код писать не захотел, что понятно, выглядеть будет это сильно хуже. Дискуссия пошла по кругу. На сем позволю себе откланяться.
Здравствуйте, Poopy Joe, Вы писали:
Doc>>Ну учитывая как ты придирался к моему коду, то в твоем нет проверок. И у тебя он не компилится и не работает PJ>В моем коде есть ошибка поэтому он даже не компилится, в твоем ест ошибки, но он компилится и работает неверно. Это достижение.
Ну так твой тоже работает не верно
Doc>>Если ты считаешь что это проблема именно контрактов, то о чем мы вообще тогда? PJ>Это будет странно, если ты пишешь тип, и совершенно нормально, когда ты пишешь однотипные контракты в 100500 мест.
Тебе уже не только я сказал — одинаковые контракты на методах это очень не частое явление. С тем же успехом ты можешь каждый if в коде заворачивать в класс. Но ты придумал себе что-то, сделал из этих фантазий выводы ...
PJ>Одно и то же условие проверятся в 3х местах, т.е. нарушаются все приличные практики.
Придуманные тобой?
PJ>В общем, корректного возражения как можно сломать тип ты предъявить не смог. Код писать не захотел, что понятно, выглядеть будет это сильно хуже. Дискуссия пошла по кругу. На сем позволю себе откланяться.
Согласен, остановимся на том, что ты пишешь кучу лишнего кода на каждый if Который кстати еще и поддержки требует. Но как я понял, ты эти классы даже не тестируешь из соображений типа "а чему там ломаться". Так что удачи
S>>Как сейчас? Использовать старые добрые исключения или же переходить на сабж? PJ>Нет. Вместо проверки значения типа в рантайме лучше использовать в параметрах тип в котором недопустимые значения просто невозможны.
А можно увидеть реальный проект, где используется подобный подход на полную катушку? Желательно C# конечно, но можно и на Хаскеле, если на шарпе нет.
Я как-то пробовал нечто подобное у себя реализовать, получилось неудобно и я быстро вернулся к обычным проверкам. Может неправильно готовил
Здравствуйте, Doc, Вы писали:
Doc>Ну так твой тоже работает не верно
Ты не просил рабочий, ты просил сигнатуры, чтобы передать идею. Вот как раз код более чем очевидно передает идею, что ошибка будет видна во время компиляции.
Doc>Тебе уже не только я сказал — одинаковые контракты на методах это очень не частое явление. С тем же успехом ты можешь каждый if в коде заворачивать в класс. Но ты придумал себе что-то, сделал из этих фантазий выводы ...
Не очень частое, однако встретилось в первом же случае, причем придуманном тобой, причем чтобы показать обратное.
И так кстати все обсуждение.
Ты заявляешь, что код на типах легко сломать и не можешь это продемонстрировать, предъявляя свой код, который сломать легко.
Ты заявляешь, что пользователю будет непонятно и надо будет смотреть метод, и предъявляешь код который в котором контракт можно узнать только просмотрев сам метод, который может быть обфусцирован или контракты вообще удалены в релизе.
Ты заявляешь, что проверок надо больше и копипастишь код, а вторую часть где проверок еще больше вообще стесняешься привести.
Ты заявляешь, что ошибки труднее увидеть и приводишь код с ошибками, который "по крайней мере компилируется", т.е. скрывает ошибку до времени исполнения.
Итд...
Я уж даже не знаю как еще очевиднее можно показать кривость контрактов.
PJ>>Одно и то же условие проверятся в 3х местах, т.е. нарушаются все приличные практики. Doc>Придуманные тобой?
Практики не копипастить код придуманы мной?
Здравствуйте, ionoy, Вы писали:
I>А можно увидеть реальный проект, где используется подобный подход на полную катушку? Желательно C# конечно, но можно и на Хаскеле, если на шарпе нет. I>Я как-то пробовал нечто подобное у себя реализовать, получилось неудобно и я быстро вернулся к обычным проверкам. Может неправильно готовил
Каким образом ты себе это представляешь? Выкладывать проекты? Я этого, разумеется, делать не буду. Неудобно у тебя, скорее всего, получается, потому что требует некоторого изменения мышления. Я могу посоветовать смотреть как это работает в функциональных языках, хоть на f#, а потом уже самому захочется переносить практики и на c#. Но на c# писать придется больше, хоть он постепенно и перенимает функциональные фичи.
Здравствуйте, Poopy Joe, Вы писали:
PJ>Здравствуйте, ionoy, Вы писали:
I>>А можно увидеть реальный проект, где используется подобный подход на полную катушку? Желательно C# конечно, но можно и на Хаскеле, если на шарпе нет. I>>Я как-то пробовал нечто подобное у себя реализовать, получилось неудобно и я быстро вернулся к обычным проверкам. Может неправильно готовил
PJ>Каким образом ты себе это представляешь? Выкладывать проекты? Я этого, разумеется, делать не буду.
То есть подобных проектов в открытом доступе нет? Есть ли на это причины?
Здравствуйте, ionoy, Вы писали:
PJ>>Каким образом ты себе это представляешь? Выкладывать проекты? Я этого, разумеется, делать не буду. I>То есть подобных проектов в открытом доступе нет? Есть ли на это причины?
Эээ... не знаю. Просто лично я в открытых проектах не участвую и не слежу за ними, соответственно помочь тебе с этим не могу.
Здравствуйте, Poopy Joe, Вы писали:
PJ>Здравствуйте, Doc, Вы писали:
Doc>>Ну так твой тоже работает не верно PJ>Ты не просил рабочий, ты просил сигнатуры, чтобы передать идею. Вот как раз код более чем очевидно передает идею, что ошибка будет видна во время компиляции.
Ну это уже совсем отмазка. Я так могу сказать что по BL email надо проверять только на null и "", а возраст может быть отрицательным или очень большим. Ведь ты обратного тоже не просил. А показать как пишутся контракты — тот код как раз.
PJ>Не очень частое, однако встретилось в первом же случае, причем придуманном тобой, причем чтобы показать обратное.
Где? Я так подозреваю что на "Contract.Requires(user != null);" будешь тыкать. Только это такая же копипаста, как и любой if.
И кстати, если несколько методов одного класса требует вдруг одинакового, то ничего не мешает завернуть требования в private метод и использовать его во всех указаниях.
Кстати, вот это все контракт (а не куча мелких контрактов)
PJ>Ты заявляешь, что код на типах легко сломать и не можешь это продемонстрировать, предъявляя свой код, который сломать легко.
Ты как его и не сломал. Равно как и не показал откуда в классах у тебя магическим образом появится проверка "возраст меньше 0".
PJ>Ты заявляешь, что пользователю будет непонятно и надо будет смотреть метод, и предъявляешь код который в котором контракт можно узнать только просмотрев сам метод, который может быть обфусцирован или контракты вообще удалены в релизе.
Сам понял что сказал то? Сборка то может быть и без контрактов, и обфусцирована... Контракты то в исходниках останутся. А для библиотек с закрытым кодом пишется документация.
PJ>Ты заявляешь, что проверок надо больше и копипастишь код, а вторую часть где проверок еще больше вообще стесняешься привести.
Какую вторую часть? Опять твои фантазии?
PJ>Ты заявляешь, что ошибки труднее увидеть и приводишь код с ошибками, который "по крайней мере компилируется", т.е. скрывает ошибку до времени исполнения.
Ошибки опять же придумал ты. У тебя в коде нет проверок, о чем еще говорить.
PJ>>>Одно и то же условие проверятся в 3х местах, т.е. нарушаются все приличные практики. Doc>>Придуманные тобой? PJ>Практики не копипастить код придуманы мной?
Там нет копипасты. Чуть-чуть подучи мат. часть (редко, но бывает даже что 1 в 1 совпадающий по коду метод не является копипастой, вот ужас).
В итоге мы имеем твой вариант, при котором код
— раздут из расчета по классу на каждую проверку, что породит огромное число классов
— все это необходимо сопровождать, помнить какие проверки есть, а каких еще нет (чтобы не продублировать)
— на все эти классы необходимы unit test, что потребует еще код (кроме тестов на сами методы конечно).
— эти классы проверок жестко связанны с различными классами логики, поэтому менять из код надо с большой осторожностью и полной проверкой всего затронутого кода.
И все это против контрактов, где все лежит по месту, без придуманной тобой копипасты, не связывает между собой разные классы.
И это ты еще не показал как будешь проверять постусловия и инварианты Как классы будут работать в случае интерфейсов (особенно если в условии контракта участвует его же свойства).
Здравствуйте, Doc, Вы писали:
Doc>Ну это уже совсем отмазка. Я так могу сказать что по BL email надо проверять только на null и "", а возраст может быть отрицательным или очень большим. Ведь ты обратного тоже не просил. А показать как пишутся контракты — тот код как раз.
Ну вот это и будет отмазка. Так как все знают, что такое email. Если у тебя в требования только проверка на null, то ошибка в требованиях и не более того.
Я тебе указываю на очевидные проблемы. Если у тебя трудности в принятием критики, то так и скажи, чего тут кругами-то ходить?
Doc>Где? Я так подозреваю что на "Contract.Requires(user != null);" будешь тыкать. Только это такая же копипаста, как и любой if. Doc>И кстати, если несколько методов одного класса требует вдруг одинакового, то ничего не мешает завернуть требования в private метод и использовать его во всех указаниях.
Тыкал бы, не сделай МС решение этой проблемы столь неудобным, что проще смирится. За этом им лучи поноса.
Заворачивание в метод мешает во-первых нарушением принципа единой ответственности, а, во-вторых, опять требует дублирования в другом классе.
Doc>Ты как его и не сломал. Равно как и не показал откуда в классах у тебя магическим образом появится проверка "возраст меньше 0".
Ты его, для начала, и не привел. То, что ты привел ломается просто отсылкой случайных параметров. Компилятор даже не пикнет.
Doc>Сам понял что сказал то? Сборка то может быть и без контрактов, и обфусцирована... Контракты то в исходниках останутся. А для библиотек с закрытым кодом пишется документация.
Т.е. мне либу еще и с иходниками надо отдавать? "Сам понял что сказал то?" (с)
Ты тужился доказать, что пользователю надо смотреть в мой код, но доказал ровно обратное — с контрактами легко ошибиться, если не читать документацию и не ковыряться в исходниках. Спасибо!
Doc>Какую вторую часть? Опять твои фантазии?
Использование контрактов. Сигнатуры методов сами по себе не интересны.
Doc>Ошибки опять же придумал ты. У тебя в коде нет проверок, о чем еще говорить.
У меня очевидно, что если тип создался, то он валиден. Ты, кстати, сам это признал предложив в IsAdult использовать byte вместо контракта. Это совершенно естественно и разумно. Все что тебе осталось сделать это следующий шаг и понять, что Age лучше байта.
Doc>Там нет копипасты. Чуть-чуть подучи мат. часть (редко, но бывает даже что 1 в 1 совпадающий по коду метод не является копипастой, вот ужас).
Не знаю какая там у тебя матчасть и где ты ее брал, но если на изменение одной константы, возраста, тебе надо поменять три строки, то это копипаста.
Doc>- раздут из расчета по классу на каждую проверку, что породит огромное число классов
У тебя тот же самый код, только не в классах. Затрудняя его понимание и поддержку. Проблемы "много классов" в индустрии нет. А вот пропущенные проверки есть.
Doc>- все это необходимо сопровождать, помнить какие проверки есть, а каких еще нет (чтобы не продублировать)
А ты будешь проверять байт на отрицательные значения?
Doc>- на все эти классы необходимы unit test, что потребует еще код (кроме тестов на сами методы конечно).
Даже юнит-тесты писать проще. Так как код изолирован и отвечает только за одно дело.
Doc>- эти классы проверок жестко связанны с различными классами логики, поэтому менять из код надо с большой осторожностью и полной проверкой всего затронутого кода.
Они связаны с логическими сущностями. Возраст есть возраст, он не обязательно ограничен одним контрактом, так же как байт не обязательно ограничен IsAdult.
Doc>И это ты еще не показал как будешь проверять постусловия и инварианты Как классы будут работать в случае интерфейсов (особенно если в условии контракта участвует его же свойства).
Да точно также. Метод возвращает тип. Даже странно, что это надо пояснять отдельно.
Здравствуйте, Poopy Joe, Вы писали:
PJ>Я тебе указываю на очевидные проблемы. Если у тебя трудности в принятием критики, то так и скажи, чего тут кругами-то ходить?
Так ты не туда указываешь. Я бы согласился, если бы ты сказал что это я забыл, что это моя ошибка. Но нет, ты сказал что это проблема контрактов. Хотя сделать не ту проверку можно хоть в контракте, хоть в классе. И вылезет она в обоих случаях только на этапе юнит-тестов, а не компиляции. Согласен?
PJ>Заворачивание в метод мешает во-первых нарушением принципа единой ответственности,
Ага, а заворачивание в отдельный класс — нет? Логика проверки остается в том же классе, которому она и принадлежит.
PJ>а, во-вторых, опять требует дублирования в другом классе.
Зачем, метод спокойно размещается в том же классе, что и сами контракты.
PJ>Ты его, для начала, и не привел. То, что ты привел ломается просто отсылкой случайных параметров. Компилятор даже не пикнет.
Ты тестируй тогда оба кода одинаково, а то у меня до проверок доколупался, т.к. до самих контрактов не смог.
По этой твоей логике, твой код уже сломан, т.к. не содержит проверок
Doc>>Сам понял что сказал то? Сборка то может быть и без контрактов, и обфусцирована... Контракты то в исходниках останутся. А для библиотек с закрытым кодом пишется документация. PJ>Т.е. мне либу еще и с иходниками надо отдавать? "Сам понял что сказал то?" (с)
Ты сам процитировал "А для библиотек с закрытым кодом пишется документация."
А если библиотека используется внутри команды или open source — то исходники и так у всех есть.
PJ>Ты тужился доказать, что пользователю надо смотреть в мой код, но доказал ровно обратное — с контрактами легко ошибиться, если не читать документацию и не ковыряться в исходниках. Спасибо!
Да, да, лучше написать неработающий код с кучей классов.
Doc>>Какую вторую часть? Опять твои фантазии? PJ>Использование контрактов. Сигнатуры методов сами по себе не интересны.
Обычные вызовы. Не вижу там ничего особого. Но если тебе хочется то вот:
Я понимаю что ты сейчас заорешь про try/catch, но их тут нет, т.к. обработка исключений идет на уровне всего приложения.
Я показываю просто вызовы. При правильной BL все userN получат валидные данные и исключений не будет.
var someClass = new SomeClass();
var user = this._someRepo.LoadCurrentUser();
bool isAdult = someClass.IsAdult(user.Age);
...
var user1 = this._someRepo.LoadAdultUser();
someClass.DoSomething1(user1.Email, user1.Age);
...
someClass.DoSomething2(user2);
someClass.DoSomething3(user3);
someClass.DoSomething4(user4);
someClass.DoSomething5(user5);
...
PJ>У меня очевидно, что если тип создался, то он валиден.
Абстрактное утверждение. Валидным он может быть по отношению к какому-то условию.
PJ>Ты, кстати, сам это признал предложив в IsAdult использовать byte вместо контракта. Это совершенно естественно и разумно. Все что тебе осталось сделать это следующий шаг и понять, что Age лучше байта.
Если мы можем взять готовый тип, то почему бы и нет. Byte в отличии от Age не содержит проверок и универсален.
Age тянет за собой лишний связи и лишний код.
PJ>Не знаю какая там у тебя матчасть и где ты ее брал, но если на изменение одной константы, возраста, тебе надо поменять три строки, то это копипаста.
И где ты три насчитал? (1) Есть переменная для класса. (2) можно сделать метод.
Но у тебя опять фантазии.
PJ>Проблемы "много классов" в индустрии нет. А вот пропущенные проверки есть.
Да ладно? Нет? Все так и рады когда объем кода все растет, а значит затраты на его поддержку растут ... и все этому рады? Правда?
Плюс ты все еще не показал как волшебным образом пропущенные проверки сами появятся в твоих классах.
PJ>А ты будешь проверять байт на отрицательные значения?
А ты про байт? Вроде же у тебя классы?
PJ>Даже юнит-тесты писать проще. Так как код изолирован и отвечает только за одно дело.
Чем проще? Т.е. тесты для метода + еще бонусом тесты для классов с типами.
Doc>>И это ты еще не показал как будешь проверять постусловия и инварианты Как классы будут работать в случае интерфейсов (особенно если в условии контракта участвует его же свойства). PJ>Да точно также. Метод возвращает тип. Даже странно, что это надо пояснять отдельно.
Это ты так типа не заметил про инварианты класса и интерфейсы?
Здравствуйте, Doc, Вы писали:
Doc>Так ты не туда указываешь. Я бы согласился, если бы ты сказал что это я забыл, что это моя ошибка. Но нет, ты сказал что это проблема контрактов. Хотя сделать не ту проверку можно хоть в контракте, хоть в классе. И вылезет она в обоих случаях только на этапе юнит-тестов, а не компиляции. Согласен?
Конечно контрактов, потому что не помогли выявить. Нафиг тогда они нужны? Ошибку можно сделать где угодно, но опять же:
В типе есть сделать сложнее, так как его назначение очевидно. Во-вторых, проще заметить. Первый же тест с невалидным емайлом это укажет, потому как тестируется именно он. Если ошибка нашлась, то исправить ее надо ровно в одном месте.
Doc>Ага, а заворачивание в отдельный класс — нет? Логика проверки остается в том же классе, которому она и принадлежит.
С чего вдруг рассылатель емейла малолеткам должен еще и решать кого считать малолетками?
PJ>>а, во-вторых, опять требует дублирования в другом классе.
Doc>Зачем, метод спокойно размещается в том же классе, что и сами контракты.
PJ>>Ты его, для начала, и не привел. То, что ты привел ломается просто отсылкой случайных параметров. Компилятор даже не пикнет.
Doc>Ты тестируй тогда оба кода одинаково, а то у меня до проверок доколупался, т.к. до самих контрактов не смог. Doc>По этой твоей логике, твой код уже сломан, т.к. не содержит проверок
Да, именно так. В продакшн такой код допускать нельзя. Еще смешнее приводить его пример с какой-нибудь вендинговой машиной "где большие штрафы".
Doc>Ты сам процитировал "А для библиотек с закрытым кодом пишется документация."
Ну я просто не стал тыкать тебя носом в хорошую практику не ставить свой код в зависимость от чтения документации другими. Но если ты настаиваешь...
Doc>А если библиотека используется внутри команды или open source — то исходники и так у всех есть.
Т.е. контракты уже имеют кучу ограничений?! А если нет? А если сначала внутри команды, а потом решили отдать наружу?
Doc>Да, да, лучше написать неработающий код с кучей классов.
Не копилирующийся. Да лучше.
Он вполне работающий, ошибка там в одном месте, где надо либо каст добавить, либо селект. Ну так компилятор на это и укажет, и я просто не смогу собрать невалидный код, в отличии от "хотя бы компилируется".
Doc>Обычные вызовы. Не вижу там ничего особого. Но если тебе хочется то вот:
Ну так пример кода, который может свалится в любой, потому что никаких проверок чего там в LoadCurrentUser нет. Это криво даже с контрактами. Особенно с контрактами, потому что с одной стороны ты требуешь соответствие контракту, а с другой не делаешь никаких усилий для выполнения.
PJ>>У меня очевидно, что если тип создался, то он валиден. Doc>Абстрактное утверждение. Валидным он может быть по отношению к какому-то условию.
Нет, это аксиома. Тип должен быть внутренне непротиворечив и все. Если у тебя другие условия, то и тип должен быть другим.
Doc>Если мы можем взять готовый тип, то почему бы и нет. Byte в отличии от Age не содержит проверок и универсален. Doc>Age тянет за собой лишний связи и лишний код.
Ну тогда надо вообще отказаться от типов, раз это лишний код.
Doc>И где ты три насчитал? (1) Есть переменная для класса. (2) можно сделать метод. Doc>Но у тебя опять фантазии.
Ну будут дублироваться методы, какая разница? Предложишь вообще все держать в одном классе?
Doc>Да ладно? Нет? Все так и рады когда объем кода все растет, а значит затраты на его поддержку растут ... и все этому рады? Правда? Doc>Плюс ты все еще не показал как волшебным образом пропущенные проверки сами появятся в твоих классах.
Объем кода никак не связан с количеством классов. Может хоть на ассемблере его увеличивать. Твои контракты увеличивают код — откажись от контрактов.
Doc>А ты про байт? Вроде же у тебя классы?
У меня типы. Байт ты не проверяешь, потому что знаешь его свойства. С Age все точно так же.
Doc>Чем проще? Т.е. тесты для метода + еще бонусом тесты для классов с типами.
Тем, что проверяя Age или Email ты концентрируешься на их проблемах и их граничных условиях. Когда ты пишешь тип Email ты наверняка проверишь его валидность, а с контрактом тебе это и в голову не пришло.
Doc>Это ты так типа не заметил про инварианты класса и интерфейсы?
Они ничего не меняют.
Здравствуйте, Poopy Joe, Вы писали:
PJ>Конечно контрактов, потому что не помогли выявить. Нафиг тогда они нужны?
Тогда и классы-типа нафиг, т.к. они ровным счетом так же себя поведут.
PJ>Ошибку можно сделать где угодно, но опять же: PJ>В типе есть сделать сложнее, так как его назначение очевидно.
Т.е. опять все дело в программисте. Сделал — не сделал, увидел — не увидел.
Кстати, назначение метода еще очевиднее.
PJ> Первый же тест с невалидным емайлом это укажет, потому как тестируется именно он.
Аналогично тест выявит и ошибку в контракте.
PJ> Если ошибка нашлась, то исправить ее надо ровно в одном месте.
То же самое (а в том редком случае, на который ты давишь, можно сделать метод)
Doc>>Ага, а заворачивание в отдельный класс — нет? Логика проверки остается в том же классе, которому она и принадлежит. PJ>С чего вдруг рассылатель емейла малолеткам должен еще и решать кого считать малолетками?
Согласен. По хорошему это вообще 2 разных класса. BL формирует список кому слать, а сервис рассылает по списку. BL в соответствующем методе имеет постусловие в контракте, а сервис валидирует email.
PJ>>>Ты его, для начала, и не привел. То, что ты привел ломается просто отсылкой случайных параметров. Компилятор даже не пикнет.
Твой так же делает фигню и не компилируется. Мне уже надоело тебе это повторять, поэтому претензии к коду, а не контрактам я буду игнорить.
Doc>>Ты сам процитировал "А для библиотек с закрытым кодом пишется документация." PJ>Ну я просто не стал тыкать тебя носом в хорошую практику не ставить свой код в зависимость от чтения документации другими. Но если ты настаиваешь...
Ну я понимаю что тебе надо все разжевывать, поэтому расскажу тебе что еще можно называть методы и параметры осмыслено, что уже документирует.
Кроме того, документация пишется не сколько в хелп файле, а в XML комментах, которые хорошо показывает IntelliSense.
Doc>>А если библиотека используется внутри команды или open source — то исходники и так у всех есть. PJ>Т.е. контракты уже имеют кучу ограничений?! А если нет? А если сначала внутри команды, а потом решили отдать наружу?
См. выше. Для выдачи наружу ты все равно будешь (сам или волонтеры) писать документацию и примеры. Иначе твоя либа сгинет никому не нужной.
И потом — нет смысла писать "тут есть контракт который валидирует". И для твоей схемы с типами и для контрактов пишется "параметр email — принимает корректный email адрес для отправки. Не может быть null"
PJ>Он вполне работающий, ошибка там в одном месте, где надо либо каст добавить, либо селект.
Не, не, не... раз это твои классы "не помогли выявить. Нафиг тогда они нужны?"
PJ>Ну так пример кода, который может свалится в любой, потому что никаких проверок чего там в LoadCurrentUser нет. Это криво даже с контрактами. Особенно с контрактами, потому что с одной стороны ты требуешь соответствие контракту, а с другой не делаешь никаких усилий для выполнения.
Я тебе написал что выдать правильные данные — забота этого метода. Придирайся дальше.
PJ>Ну будут дублироваться методы, какая разница? Предложишь вообще все держать в одном классе?
Все зависит от того, что это за проверка, раз она нужна много где. Что-то останется в 1 классе, а что-то может уйти в extension метод.
Doc>>А ты про байт? Вроде же у тебя классы? PJ>У меня типы. Байт ты не проверяешь, потому что знаешь его свойства. С Age все точно так же.
Байт проверен разработчиками C#. Я доверяю С# и используемым библиотекам. Свое — тестируется.
А ты выходит просто доверяешь своим классам и не тестируешь их? Ну да, ну да...
Doc>>Чем проще? Т.е. тесты для метода + еще бонусом тесты для классов с типами. PJ>Тем, что проверяя Age или Email ты концентрируешься на их проблемах и их граничных условиях. Когда ты пишешь тип Email ты наверняка проверишь его валидность, а с контрактом тебе это и в голову не пришло.
Ну по твоей такой логике в твоем примере ты вообще ничего не проверил. Значит у тебя вообще пусто?
Doc>>Это ты так типа не заметил про инварианты класса и интерфейсы? PJ>Они ничего не меняют.
Да ну... Ну покажи инвариант для интерфейса.
public interface IDemo {
byte A { get; set; }
byte B { get; set; }
byte C { get; set; }
}
Инвариант — сумма A+B+C < 200
1) 1 имплементацию пишешь ты
2) Там как видишь public и юзер легко может написать свою, как его заставишь соблюдать?
Здравствуйте, Doc, Вы писали:
PJ>>Конечно контрактов, потому что не помогли выявить. Нафиг тогда они нужны? Doc>Тогда и классы-типа нафиг, т.к. они ровным счетом так же себя поведут.
Нет, не так же.
Doc>Т.е. опять все дело в программисте. Сделал — не сделал, увидел — не увидел. Doc>Кстати, назначение метода еще очевиднее.
Ну понятно, что всегда дело в программисте. Но, в одном случае компилятор может помочь, а в другом программист будет рассказывать как важно составлять правильную документацию и отдавать сорцы либы.
PJ>> Первый же тест с невалидным емайлом это укажет, потому как тестируется именно он.
Doc>Аналогично тест выявит и ошибку в контракте.
Ага, и так тесты для всех месте где есть емайл в контрактах. Не копипаст, ага.
Doc>Согласен. По хорошему это вообще 2 разных класса. BL формирует список кому слать, а сервис рассылает по списку. BL в соответствующем методе имеет постусловие в контракте, а сервис валидирует email.
Че то ты сам себе противоречишь. Какой отдельный класс, их надо экономить.
Doc>Твой так же делает фигню и не компилируется. Мне уже надоело тебе это повторять, поэтому претензии к коду, а не контрактам я буду игнорить.
Так это и есть претензии. У меня фигня не компилируется,а у тебя фигня копилируется. А чего ты еще от компилятора ждешь? Что за тебя код писать будет?
Doc>Ну я понимаю что тебе надо все разжевывать, поэтому расскажу тебе что еще можно называть методы и параметры осмыслено, что уже документирует. Doc>Кроме того, документация пишется не сколько в хелп файле, а в XML комментах, которые хорошо показывает IntelliSense.
А... Ну понятно, а если использовать венгерскую нотацию, то вообще можно на типы забить...
Doc>См. выше. Для выдачи наружу ты все равно будешь (сам или волонтеры) писать документацию и примеры. Иначе твоя либа сгинет никому не нужной. Doc>И потом — нет смысла писать "тут есть контракт который валидирует". И для твоей схемы с типами и для контрактов пишется "параметр email — принимает корректный email адрес для отправки. Не может быть null"
А зачем тогда вообще все ооп, паттрерны, компиляторы? Да ограничиться одним фортраном и писать документацию. Навыдумывали ерунды...
Doc>Не, не, не... раз это твои классы "не помогли выявить. Нафиг тогда они нужны?"
Так помогли. В отличии от твоих контрактов.
Doc>Я тебе написал что выдать правильные данные — забота этого метода. Придирайся дальше.
Что такое "правильные данные"? Там три разных контракта. Тут даже придираться не надо, все очевидно. Ты сам отлично закапываешь свои контракты.
Doc>Все зависит от того, что это за проверка, раз она нужна много где. Что-то останется в 1 классе, а что-то может уйти в extension метод.
Как опять отдельный класс? Я не ослышался?
Doc>Байт проверен разработчиками C#. Я доверяю С# и используемым библиотекам. Свое — тестируется. Doc>А ты выходит просто доверяешь своим классам и не тестируешь их? Ну да, ну да...
Ну так и говори, что ты доверяешь разработчикам c#, а себе нет. Причем как-то странно. С контрактами доверяешь, причем даже с очевидными ошибками.
Doc>Ну по твоей такой логике в твоем примере ты вообще ничего не проверил. Значит у тебя вообще пусто?
В моей логике проверки внутренние. А в твоей логике они непонятно где. Вариант, что один метод вернет верные данные для трех взаимоисключающих контрактов — смешон.
Doc>Да ну... Ну покажи инвариант для интерфейса. Doc>
public interface IDemo {
Doc> byte A { get; set; }
Doc> byte B { get; set; }
Doc> byte C { get; set; }
Doc>}
Doc>Инвариант — сумма A+B+C < 200
И чем это вариант отличается от твоего предыдущего примера с возрастом и весом?
Doc>1) 1 имплементацию пишешь ты Doc>2) Там как видишь public и юзер легко может написать свою, как его заставишь соблюдать?
Хм... так предлагаешь писать реализацию IDemo с инвариантом?
Не, ну против дурака с клавиатурой приема конечно нет. Сломать можно все.
Но в прошлом сообщении я тебе сказал, что правильное использование типов основано на аксиоме непротиворечивости типа, что в твоем случае не выполняется. В данном случае проблема находится за пределами и типов, и контрактов.
Здравствуйте, Poopy Joe, Вы писали:
Doc>>Тогда и классы-типа нафиг, т.к. они ровным счетом так же себя поведут. PJ>Нет, не так же.
Ты выше написал что так же. Не отпирайся
PJ>Ну понятно, что всегда дело в программисте. Но, в одном случае компилятор может помочь, а в другом программист будет рассказывать как важно составлять правильную документацию и отдавать сорцы либы.
У тебя код без единого условия и компиялятор не помог. Так что по твоей же логике "нафиг классы".
PJ>Ага, и так тесты для всех месте где есть емайл в контрактах. Не копипаст, ага.
Если вдруг тест будет нужен больше раза — extension метод не запрещали.
PJ>Так это и есть претензии. У меня фигня не компилируется,а у тебя фигня копилируется. А чего ты еще от компилятора ждешь? Что за тебя код писать будет?
Да, и прям в ошибках компилятора будет указано что ты логику не написал? или что-то иное?
Так что не выдавай желаемое за действительное.
Doc>>Ну я понимаю что тебе надо все разжевывать, поэтому расскажу тебе что еще можно называть методы и параметры осмыслено, что уже документирует. Doc>>Кроме того, документация пишется не сколько в хелп файле, а в XML комментах, которые хорошо показывает IntelliSense. PJ>А... Ну понятно, а если использовать венгерскую нотацию, то вообще можно на типы забить...
Ну если для тебя осмысленные названия параметров это как венгерская нотация (которая кстати не отменяет типы, а описывает их)...
Твоему коду поди и обфускация не нужна
PJ>А зачем тогда вообще все ооп, паттрерны, компиляторы? Да ограничиться одним фортраном и писать документацию. Навыдумывали ерунды...
Как одно отменяет другое? Уже бред несешь. IntelliSense (документацией) я так понимаю ты вообще не пользуешся? В блокноте пишешь?
Doc>>Не, не, не... раз это твои классы "не помогли выявить. Нафиг тогда они нужны?" PJ>Так помогли. В отличии от твоих контрактов.
Да? Ну подтверди примером, покажи что за текст ошибки будет. Что там про логику проверки написано
Doc>>Я тебе написал что выдать правильные данные — забота этого метода. Придирайся дальше. PJ>Что такое "правильные данные"? Там три разных контракта. Тут даже придираться не надо, все очевидно. Ты сам отлично закапываешь свои контракты.
Только в твоей фантазии.
Doc>>Все зависит от того, что это за проверка, раз она нужна много где. Что-то останется в 1 классе, а что-то может уйти в extension метод. PJ>Как опять отдельный класс? Я не ослышался?
Нет.
PJ>Ну так и говори, что ты доверяешь разработчикам c#, а себе нет.
Это понимать как признание что ты не доверяешь разработчикам C# и базовые типы тестишь?
Doc>>Ну по твоей такой логике в твоем примере ты вообще ничего не проверил. Значит у тебя вообще пусто? PJ>В моей логике проверки внутренние. А в твоей логике они непонятно где. Вариант, что один метод вернет верные данные для трех взаимоисключающих контрактов — смешон.
Один метод? Ты код точно читать умеeшь или с фантазиями?
PJ>Хм... так предлагаешь писать реализацию IDemo с инвариантом?
У тебя с этим сложность?
PJ>Не, ну против дурака с клавиатурой приема конечно нет. Сломать можно все.
Ну вот тебе код. И жду от тебя аналог на классах-типах.
Первый пример на интерфейсах. Тут чуть сложнее, т.к. прямой поддержки инвариантов для интерфейсов нет. Но тем не менее все реализации интерфейса будут соблюдать указанное правило:
[ContractClass(typeof(DemoContract))]
public interface IDemo
{
byte A { get; set; }
byte B { get; set; }
byte C { get; set; }
}
[ContractClassFor(typeof(IDemo))]
internal abstract class DemoContract : IDemo
{
public byte A
{
get { return default(byte); }
set { Contract.Requires(DemoContractInvariant.ClassInvariant(value, this.B, this.C)); }
}
public byte B
{
get { return default(byte); }
set { Contract.Requires(DemoContractInvariant.ClassInvariant(this.A, value, this.C)); }
}
public byte C
{
get { return default(byte); }
set { Contract.Requires(DemoContractInvariant.ClassInvariant(this.A, this.B, value)); }
}
}
public static class DemoContractInvariant
{
[Pure]
public static bool ClassInvariant(byte a, byte b, byte c)
{
return a + b + c < 200;
}
}
Ну и использование:
public class Demo1 : IDemo
{
public byte A { get; set; }
public byte B { get; set; }
public byte C { get; set; }
}
...
IDemo d1 = new Demo1();
d1.A = 100;
d1.B = 99;
d1.C = 100;
В случае с абстрактными классами все проще:
public abstract class Demo
{
public byte A { get; set; }
public byte B { get; set; }
public byte C { get; set; }
[ContractInvariantMethod]
private void ObjectInvariant()
{
Contract.Invariant(this.A + this.B + this.C < 200);
}
}
public class Demo2 : Demo
{
}
...
Demo d2 = new Demo2();
d2.A = 100;
d2.B = 99;
d2.C = 100;
Здравствуйте, Doc, Вы писали:
Doc>У тебя код без единого условия и компиялятор не помог. Так что по твоей же логике "нафиг классы".
Помог.
Doc>Если вдруг тест будет нужен больше раза — extension метод не запрещали.
Если он вдруг понадобится второй раз, то другой сотрудник о твоем контракте и знать-то не будет. Он тупо добавить еще одну проверку в свой метод.
Doc>Да, и прям в ошибках компилятора будет указано что ты логику не написал? или что-то иное?
Да вот прям так и напишет. Что типы не приводятся. Вся логика по сути это и есть трансформация типов. Здорово, правда?!
Doc>Ну если для тебя осмысленные названия параметров это как венгерская нотация (которая кстати не отменяет типы, а описывает их)... Doc>Твоему коду поди и обфускация не нужна
Осмысленные названия никак не заменяют проверку типов компилятором. Удивительно, что ты тут вообще взялся спорить.
Doc>Как одно отменяет другое? Уже бред несешь. IntelliSense (документацией) я так понимаю ты вообще не пользуешся? В блокноте пишешь?
Я не говорил, что оно отменяет, это из твоих слов следует. Количество классов считаешь, типизацию заменяешь на "правильное название параметров" итд.
Doc>>>Я тебе написал что выдать правильные данные — забота этого метода. Придирайся дальше. PJ>>Что такое "правильные данные"? Там три разных контракта. Тут даже придираться не надо, все очевидно. Ты сам отлично закапываешь свои контракты.
Doc>Только в твоей фантазии.
Я не фантазировал твой пример. Ты его сам привел и сам привел код, который наилучшим просто способом демонстрирует бесполезность контрактов. Ничего лучшего и просить было нельзя.
Doc>Это понимать как признание что ты не доверяешь разработчикам C# и базовые типы тестишь?
Хмм... даже не представляю как можно было сделать такой вывод.
Doc>Один метод? Ты код точно читать умеeшь или с фантазиями?
У тебя один LoadUser. Насчитай там больше.
Doc>Ну вот тебе код. И жду от тебя аналог на классах-типах.
Ну тут детали зависят от некоторых условий. Если это внутренний тип, то он просто выкидывается, как глупый. Ну и, понятно, только идиот может написать интерфейс с публичным сеттером и инвариантом на три из них. Если интерфейс внешний и требует инвариант, то тут конечно и надо что-то писать.
public class DemoContainer<T> where T : IDemo
{
readonly IDemo _demo
DemoContainer() {};
public static Optional<DemoContainer> Create(IDemo demo)
=> demo.a + demo.b + demo.c < 200 ? Optional.Create(new DemoContainer(demo)) : Optional.Nothing();
}
иcпользование просто
void DemoConsumer(DemoContainer<IDemo> demo) {}
К сожалению readonly не гарантирует, что изменять _demo нельзя, поэтому DemoContainer не дает доступа к _demo, в лишь к его геттерам. Писать все лениво, но идея и так понятна.
Не помог. (если помог, показывай где сообщение об ошибке в логике)
PJ>Если он вдруг понадобится второй раз, то другой сотрудник о твоем контракте и знать-то не будет. Он тупо добавить еще одну проверку в свой метод.
C тем же успехом можно и копию класса сделать. (а дубли кода, кстати, VS искать умеет).
Doc>>Да, и прям в ошибках компилятора будет указано что ты логику не написал? или что-то иное? PJ>Да вот прям так и напишет. Что типы не приводятся. Вся логика по сути это и есть трансформация типов. Здорово, правда?!
Не выкручивайся (да еще так не умело). Что не приводятся типы не говорит о том, что есть ошибка в проверке.
Твой код соберется или не соберется с ошибкой приведения, не зависит от того проверяется email только на null или на валидность.
PJ>Осмысленные названия никак не заменяют проверку типов компилятором. Удивительно, что ты тут вообще взялся спорить.
Опять споришь сам с собой и лупишь себя по лицу.
Doc>>>>Я тебе написал что выдать правильные данные — забота этого метода. Придирайся дальше. PJ>>>Что такое "правильные данные"? Там три разных контракта. Тут даже придираться не надо, все очевидно. Ты сам отлично закапываешь свои контракты.
Опять словоблудие, непонятная радость и ноль фактов. Подскажу, там если поглядишь специально показано что передаются разные переменные. Дальше разжёвывать не буду.
Doc>>Один метод? Ты код точно читать умеeшь или с фантазиями? PJ>У тебя один LoadUser. Насчитай там больше.
Погляди на текст и имена переменных.
PJ>Ну тут детали зависят от некоторых условий. Если это внутренний тип, то он просто выкидывается, как глупый.
Ничего выкидывать не надо. Пример интерфейса для простоты. Если не нравится — нарисуй свой интерфейс, где в инварианте есть связь между некоторыми параметрами, которые могут быть изменены из вне.
PJ> public static Optional<DemoContainer> Create(IDemo demo) PJ> => demo.a + demo.b + demo.c < 200 ? Optional.Create(new DemoContainer(demo)) : Optional.Nothing(); PJ>}
И что помешает другому программисту инициализировать и передать свою реализацию в метод который принимает IDemo? В случае с контрактом, можешь написать свою реализацию, можешь уточнить контракт (сделав его строже), то соблюдать оригинальный контракт будут все реализации.
Здравствуйте, Doc, Вы писали:
Doc>Не помог. (если помог, показывай где сообщение об ошибке в логике)
Неверный тип и есть ошибка в логике. Сколько раз еще надо это повторить?
Doc>C тем же успехом можно и копию класса сделать. (а дубли кода, кстати, VS искать умеет).
Копии классов видны в иерархии, а какие-то строки внутри методов — нет. Ничего не помешает конечно, но вероятность, с контрактами, выше.
Doc>Твой код соберется или не соберется с ошибкой приведения, не зависит от того проверяется email только на null или на валидность.
Не зависит. Но если email написан правильно, то он будет работать везде одинаково, вне зависимости от того, забыл пользователь в контракте проверить или нет. Если данных используются более, чем в одном вызове, то проверка будет сделана один раз, при создании, а не в каждом методе.
Doc>Опять споришь сам с собой и лупишь себя по лицу.
Это не я доказываю как здорово можно заменить проверку типов правильными названиями.
Doc>Опять словоблудие, непонятная радость и ноль фактов. Подскажу, там если поглядишь специально показано что передаются разные переменные. Дальше разжёвывать не буду.
Это у тебя словоблудие. Используется один LoadUser и три вызова подряд с взаимоисключающими контрактами. На каких-то из них он будет постоянно валится.
Ты привел пример программы которая только и делает, что валится. Че тут разжевывать? И так все понятно.
Doc>Погляди на текст и имена переменных.
Названия переменных не помешают им свалится.
Doc>Ничего выкидывать не надо. Пример интерфейса для простоты. Если не нравится — нарисуй свой интерфейс, где в инварианте есть связь между некоторыми параметрами, которые могут быть изменены из вне.
Ну да пример глупого интерфейса, с глупыми требованиями. Я уже говорил, что от дураков защиты нет. В таком случае код надо оборачивать и изолировать, а не контракты лепить к нему.
PJ>> public static Optional<DemoContainer> Create(IDemo demo) PJ>> => demo.a + demo.b + demo.c < 200 ? Optional.Create(new DemoContainer(demo)) : Optional.Nothing(); PJ>>}
Doc>И что помешает другому программисту инициализировать и передать свою реализацию в метод который принимает IDemo? В случае с контрактом, можешь написать свою реализацию, можешь уточнить контракт (сделав его строже), то соблюдать оригинальный контракт будут все реализации.
Ошибка компилятора, я полагаю, так как метод не принимает IDemо. Всегда ваш, КО.
Здравствуйте, Poopy Joe, Вы писали:
Doc>>Не помог. (если помог, показывай где сообщение об ошибке в логике) PJ>Неверный тип и есть ошибка в логике. Сколько раз еще надо это повторить?
До тех пока пока ты не покажешь логическую цепочку от "не приводится тип" до "нет та проверка email".
Doc>>Твой код соберется или не соберется с ошибкой приведения, не зависит от того проверяется email только на null или на валидность. PJ>Не зависит. Но если email написан правильно, то он будет работать везде одинаково, вне зависимости от того, забыл пользователь в контракте проверить или нет.
Еще раз как "не приводится тип" обозначает "нет та проверка email"?
Doc>>Погляди на текст и имена переменных. PJ>Названия переменных не помешают им свалится.
Ну хорошо — найди в коде что все они загружаются одним методом? Не нашел? И не фантазируй.
PJ>Ну да пример глупого интерфейса, с глупыми требованиями.
Это просто пример для разбора. Написать у тебя не вышло, пошла в ход демагогия.
PJ>Я уже говорил, что от дураков защиты нет. В таком случае код надо оборачивать и изолировать, а не контракты лепить к нему.
Ну сознайся что инвариант для интерфейса на типах ты не напишешь. Ты можешь контролировать входные параметры методов через пачку типов.
Но как только появляется между ними связь, то все — твоя схема не работает.
PJ>Ошибка компилятора, я полагаю, так как метод не принимает IDemо. Всегда ваш, КО.
Не тупи Речь о методах, которые потребляют интерфейс. Не обертки, не конкретные реализации, а интерфейсы.
Здравствуйте, Doc, Вы писали:
Doc>До тех пока пока ты не покажешь логическую цепочку от "не приводится тип" до "нет та проверка email".
Чего в предложении "тип должен заботится о своем инварианте" ты не понял? Вроде не такая уж и сложная мысль. Тут не надо никаких логических цепочек — это аксиома.
Doc>Ну хорошо — найди в коде что все они загружаются одним методом? Не нашел? И не фантазируй.
Я нашел два метода на пять вызовов.
var user = this._someRepo.LoadCurrentUser();
var user1 = this._someRepo.LoadAdultUser()
Ни один из них не отвечает твоим же требованиям даже по названию. Мне не надо фантазировать. Фантазировать сейчас надо тебе, рассказывая как LoadAdultUser в одном случае загрузит блондинок, в во втором толстушек. Начинай...
Doc>Это просто пример для разбора. Написать у тебя не вышло, пошла в ход демагогия.
Ну так глупый пример. Код я тебе написал, но пример все равно глупый. Но код ты не заметил, зато ударился в демагонию, это верно.
Doc>Ну сознайся что инвариант для интерфейса на типах ты не напишешь. Ты можешь контролировать входные параметры методов через пачку типов. Doc>Но как только появляется между ними связь, то все — твоя схема не работает.
Зачем мне сознаваться, если я тебе написал? Ты, как ребенок, уже начала клянчить чего-то что ли?
Doc>Не тупи Речь о методах, которые потребляют интерфейс. Не обертки, не конкретные реализации, а интерфейсы.
Это как рассуждать о пользе таблеток от головной боли, если биться головой об стену.
Так не делай методы которые потребляют такие интерфейсы.
Если это внутренний интерфейс, то его надо поменять, если внешний, то обернуть. Вот чего не надо делать, так это реализовывать его с костылями.
Здравствуйте, Sinix, Вы писали:
S>При всём уважении — забейте. Оппонент то ли троллит, то ли не считает нужным читать ответы. В любом их вариантов лучше не кормить.
Соглашусь с вами. Хотя на троллинг это уже не похоже, уж сильно толсто (только на эмоциях).
Здравствуйте, Poopy Joe, Вы писали:
Doc>>До тех пока пока ты не покажешь логическую цепочку от "не приводится тип" до "нет та проверка email". PJ>Чего в предложении "тип должен заботится о своем инварианте" ты не понял? Вроде не такая уж и сложная мысль. Тут не надо никаких логических цепочек — это аксиома.
Ты я так понимаю принципиально не хочешь ответить на вопрос как одна ошибка из другой следует.
Doc>>Ну сознайся что инвариант для интерфейса на типах ты не напишешь. Ты можешь контролировать входные параметры методов через пачку типов. Doc>>Но как только появляется между ними связь, то все — твоя схема не работает. PJ>Зачем мне сознаваться, если я тебе написал? Ты, как ребенок, уже начала клянчить чего-то что ли?
Твой пример работает только для методов, которые принимают обертку. Заставить пользователя, написавшего свою реализацию, соблюдать условия у тебя не вышло. Так что приведенный код только и доказывает что инвариант для интерфейса ты не напишешь.
В общем, учитывая что отвечать на вопросы ты не хочешь, некорректный код (который не решает задачу) ты выдаешь как корректный, и вместо того, чтобы показать свой метод в действии начинаешь демагогию о глубинных смыслах демокода, то смысла общаться дальше я не вижу.
Здравствуйте, Doc, Вы писали:
Doc>Ты я так понимаю принципиально не хочешь ответить на вопрос как одна ошибка из другой следует.
Я ответил. Если ты ответ не хочешь замечеть, потому что на него тебе возразить нечего, то я тут ничем помочь не могу.
Ты прекрасно закопал контракты.