Здравствуйте, 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-у