GIV>Проблема как раз в том можем ли мы сформулировать этот самый инвариант так, чтоб покрыть все возможные сценарии использования
объекта и при этом с уважением отнестись к SRP...
Не можем. И не должны. Бизнес-логика определяет.
Здравствуйте, http://code-inside-out.blogspot.com/, Вы писали:
HCI>Здравствуйте, Mike Chaliy, Вы писали:
MC>>Интерсно мнение, может ли идеологически сеттер кидать исключение?
HCI>Если говорить _идеологически_, то нет, исключение в сеттере — это некошерно.
Здравствуйте, _FRED_, Вы писали:
U>>Поэтому проверок в сеттерах быть не должно, проверки корректности состояния объекта должны производиться либо по месту действия, либо специальным методом вроде IsValidObject().
_FR>…и решение данной проблемы оказалось ортогональным валидации. То есть, получается, каждому уровню валидации — своё место и своё поведение, свой результат.
А по существу? Я привел самый простой случай взаимозависящих свойств. Покажи как решить задачу изменения взаимозависящих свойств, используя проверки в сеттерах (с использованием DependencyObject или нет не важно). Я решения этой задачи с использованием проверок в сеттерах не вижу.
_FR>И на частном примере "вот так вот плохо", вывод что "вообще нельзя" сомнителен.
Т.е. ты предлагаешь для невзаимозависящих свойств пользоваться проверками в сеттерах, а для взаимозависящих каким-то другим механизмом?
Иногда можно и так, но тут лежат грабли. Допустим были у нас невзаимозависящие свойства, мы написали такой код:
Country Country
{
set { this.country = value; }
}
double Age
{
set
{
if (value < 18)
throw new InvalidOperation();
}
}
//Использование
drinkPerson.Age = 20;
drinkPerson.Country = Country.Russia;
Все работает, все прекрасно. Однако завтра задача усложнилась, теперь нужно чтобы возраст был не менее 18, а не менее country.MinDrinkAge. И что теперь делать? Если напишем так:
Country Country
{
set
{
if (Age < value.MinDrinkAge)
throw new InvalidOperation();
}
}
double Age
{
set
{
if (value < Country.MinDrinkAge)
throw new InvalidOperation();
}
}
бросит исключение, если, к примеру, в старом состоянии страна была установлена в Usa, где возраст распития спиртных напитков 21, соответственно как изменять состояние объекта с такими проверками непонятно.
Поэтому от проверок в сеттерах в этом случае придется отказаться, введя функцию ValidateObject(), однако ранее написанный код:
Стал теперь работать неправильно, т.к. в нем вызова ValidateObject() нет. Т.е. изменение внутренней логики проверки валидности класса Person повлекло за собой необходимость изменения кода использующего этот класс. Это весьма сомнительное удовольствие, чреватое внесением багов при изменении кода.
гарантирует, что изменение внутренней логики проверки валидности класса Person не повлечет за собой необходимость изменения кода использующего этот класс.
_FR>Это, например, неплохо решено в DependencyObject…
Честно говоря, не понял, как DependencyObject решает проблему изменения взаимозависящих свойств.
U>>Хотя правильнее всего сеттеры вообще не писать, чем меньше в коде сеттеров, тем проще он читается.
_FR>Уже не раз обсуждалось, что immutable-модель плохо совместима с дизайнерами (за все дизайнеры на свете не поручусь, не знаю, но за дотнетовские — да) компонентов.
Т.е. дизайнеры вынуждают нас писать плохой код. Далее уже нужно смотреть, что для нас в конкретном случае важнее — более высокое качество кода или возможность использования дизайнера.
_FR>Даже структуры (вопреки рекомендациям), даже с перегруженными Equals\GetHashCode (вопреки здравому смыслу) разработчики FCL, , сделали изменяемыми (System.Drawing.Size и System.Windows.Size).
Т.е. разработчики заботливо разложили грабли. Им за это нужно поаплодировать?
Re[4]: Проверки в сеттерах приводят к дублированию кода
_FR>>Это, например, неплохо решено в DependencyObject…
U>Честно говоря, не понял, как DependencyObject решает проблему изменения взаимозависящих свойств.
Там это делаеться колбеками, типа как тока значение поменялось дергаеться метод. Ничем не отличаеться от проверки в сеттере. Было введено как костыль, так как значение свойства в DependencyObject можно устаналивать минуя сеттер.
Mike Chaliy wrote:
> Интерсно мнение, может ли идеологически сеттер кидать исключение?
Сеттер обязан кидать исключение, если какой-то аргумент может привести к
дальнейшей ошибке ПРЯМО СЕЙЧАС. Например, в аргументах передали какой-то
объект obj, а мы у этого объекта в сеттере забираем идентификатор или
значение obj.getId() — поэтому мы в самом начале сеттера ставим
Assert.notNull(obj) в случае приватного или внутреннего публичного
метода, или throw new IllegalArgumentException в случае внешнего
публичного метода (SJCP).
Проверки на диапазоны, допустимые значения, размеры и прочее НЕ должны
быть в сеттерах. Не забываем про принцип Single Responsibility
Principle, а также про то, что мы должны стремиться к тому, что весь
код, который может работать на основе публичного контракта класса должен
быть вынесен за пределы класса.
Поэтому мы имеем следующую схему:
1. Сущность, которая хранит значения и ловит уж совсем откровенные косяки.
2. Спецификация сущности. В спецификации мы задаем правила определяющие
валидность сущности. Правила могут быть как очень простыми (login !=
null), более сложными (match("\\w+", name), так и составными (password
== passwordConfirmation). Спецификация может храниться мильоном
способов: жестко-закодированном, во внешнем XML конфиге, в скрипте, в
виде аннотаций сущности и т.д.
3. Валидатор. Данный элемент перед сущность и ее спецификацию, и
начинает проверку. После чего выдает список несоответствий с кодами.
Для сущностей domain layer можно вызывать валидатор в DAO слое перед
сохранением и после загрузки сущностей и возбуждать исключение в случае
наличия хотя бы одной ошибки.
Posted via RSDN NNTP Server 2.1 beta
Re[5]: Проверки в сеттерах приводят к дублированию кода
Здравствуйте, Mike Chaliy, Вы писали:
MC>так как значение свойства в DependencyObject можно устаналивать минуя сеттер.
Точнее даже не так. Я вообще не знаю чтобы ипользовало этот бедный сеттер. Биндинг не использует, в постороении дерева тоже свойства не учавствуют. Вобщемто в DependencyObject сеттеры, это так — для красоты.
Здравствуйте, mazurkin, Вы писали:
M>Mike Chaliy wrote:
>> Интерсно мнение, может ли идеологически сеттер кидать исключение?
M>Сеттер обязан кидать исключение, если какой-то аргумент может привести к M>дальнейшей ошибке ПРЯМО СЕЙЧАС.
Угу, хороший ответ. Потдержка нескольких инвариантов обьекта.
Mike Chaliy wrote:
> M>Сеттер обязан кидать исключение, если какой-то аргумент может привести к > M>дальнейшей ошибке ПРЯМО СЕЙЧАС. > > Угу, хороший ответ. Потдержка нескольких инвариантов обьекта. > SJCP — это что?
Я хотел сказать, что вопрос про выбор альтернативы в виде assert'ов и
IllegalArgumentException'ов в методах задается в экзамене SCJP (Sun
Certified Java Programmer)
Здравствуйте, mazurkin, Вы писали:
M>Mike Chaliy wrote:
>> M>Сеттер обязан кидать исключение, если какой-то аргумент может привести к >> M>дальнейшей ошибке ПРЯМО СЕЙЧАС. >> >> Угу, хороший ответ. Потдержка нескольких инвариантов обьекта. >> SJCP — это что?
M>Я хотел сказать, что вопрос про выбор альтернативы в виде assert'ов и M>IllegalArgumentException'ов в методах задается в экзамене SCJP (Sun M>Certified Java Programmer)
Аха, я так и нашел, просто не мог связать.
А еще вопрос. Assert.notNull(obj) попадает в релизные билды?
Mike Chaliy wrote:
> А еще вопрос. Assert.notNull(obj) попадает в релизные билды?
Вообще вопрос совершенно неоднозначный и весьма интересный, по крайней
мере потому, что в свое время много над ним думал
Если мы обратимся к истории и вспомним MSVC+/Borland Delphi и иже с
ними, то увидим, что там существует четкое разделение release и debug
билдов. И в debug билдах ассерты действительно отключаются — в C++ через
макросы, в Delphi — вроде через сам язык.
Меня все время это очень удивляло. Я нахожу в этом некое недальновидное
лицемерие.
То есть мы поставляем клиенту продукт с отключенной системой внутренних
проверок в надежде на то, что если ошибки и возникнут, то в отсутствие
ассертов у нас всплывет не четкий и понятный баннер с информацией о том,
что мы облажались и где допустили такую ситуацию; а абстрактное окошко
об ошибке в модуле kerner32.dll и можно будет все списать на "глючную
винду", а то глядишь и все само собой рассосется. И я бы понял, если бы
отключение ассертов как то влияло на производительность, но ведь
банальные проверки на фоне других операций никаких задержек не вносят.
Посему я сторонник жесткой ответственности — внутренние проверки не
отрубать вообще никогда, кроме тех, что реально влияют на
производительность.
Здравствуйте, mazurkin, Вы писали:
M>Mike Chaliy wrote:
>> А еще вопрос. Assert.notNull(obj) попадает в релизные билды?
M>Вообще вопрос совершенно неоднозначный и весьма интересный, по крайней M>мере потому, что в свое время много над ним думал
M>Если мы обратимся к истории и вспомним MSVC+/Borland Delphi и иже с M>ними, то увидим, что там существует четкое разделение release и debug M>билдов. И в debug билдах ассерты действительно отключаются — в C++ через M>макросы, в Delphi — вроде через сам язык.
M>Меня все время это очень удивляло. Я нахожу в этом некое недальновидное M>лицемерие.
M>То есть мы поставляем клиенту продукт с отключенной системой внутренних M>проверок в надежде на то, что если ошибки и возникнут, то в отсутствие M>ассертов у нас всплывет не четкий и понятный баннер с информацией о том, M>что мы облажались и где допустили такую ситуацию; а абстрактное окошко M>об ошибке в модуле kerner32.dll и можно будет все списать на "глючную M>винду", а то глядишь и все само собой рассосется. И я бы понял, если бы M>отключение ассертов как то влияло на производительность, но ведь M>банальные проверки на фоне других операций никаких задержек не вносят.
M>Посему я сторонник жесткой ответственности — внутренние проверки не M>отрубать вообще никогда, кроме тех, что реально влияют на M>производительность.
Просто в .Нет когда говорят ассеры, обычно подразумеваться дебажные ассерты. Вот меня и заинтерисовал вопрос, как с этим доло обстоит в джаве. Быстрым поиском я этого не нашел, вот и решил спросить.
Mike Chaliy wrote:
> Просто в .Нет когда говорят ассеры, обычно подразумеваться дебажные ассерты. Вот меня и заинтерисовал вопрос, как с этим доло обстоит в джаве. Быстрым поиском я этого не нашел, вот и решил спросить.
Так я и хочу сказать — что независимо от платформы или языка считаю, что
ассерты не могут быть в каком-то отдельном релизе. Если уж поставили
ассерт, пусть он работает всегда! Или убедите меня в обратном
Здравствуйте, mazurkin, Вы писали:
M>Так я и хочу сказать — что независимо от платформы или языка считаю, что M>ассерты не могут быть в каком-то отдельном релизе. Если уж поставили M>ассерт, пусть он работает всегда! Или убедите меня в обратном
Я не очень понимаю в чем нужно убедить? Если я ставлю ассерт значит я хочу чтобы он работал только в дебаге. Если нужен аналог для всех релизов — я кидаю исключение.
Здравствуйте, Ziaw, Вы писали:
Z>Здравствуйте, mazurkin, Вы писали:
M>>Так я и хочу сказать — что независимо от платформы или языка считаю, что M>>ассерты не могут быть в каком-то отдельном релизе. Если уж поставили M>>ассерт, пусть он работает всегда! Или убедите меня в обратном
Z>Я не очень понимаю в чем нужно убедить? Если я ставлю ассерт значит я хочу чтобы он работал только в дебаге. Если нужен аналог для всех релизов — я кидаю исключение.
Я видел схемы именования дизана по контракту ввиде Ассерт.АргументНотНулл(). Ассерт же не переводится как "проверка для дебаггера", Ассерт перводиться как "Утверждение". Это как раз то чем занимаеться дизаин по контракту. А тот в свою очередь обязателен в любом релизе.
Собсно мой вопрос был в том присутвует ли эта конструкия в джаве в релизе.
Судя по этому:
Assists in validating arguments.
The class is based along the lines of JUnit. If an argument value is deemed invalid, an IllegalArgumentException is thrown.
Ziaw wrote:
> Я не очень понимаю в чем нужно убедить? Если я ставлю ассерт значит я хочу чтобы он работал только в дебаге. Если нужен аналог для всех релизов — я кидаю исключение.
Я не совсем это имел в виду.
Мне интересна мотивация — вот вы хотите чтобы ассерт работал только в
дебаге — а почему?
... MC>Я видел схемы именования дизана по контракту ввиде Ассерт.АргументНотНулл(). Ассерт же не переводится как "проверка для дебаггера", Ассерт перводиться как "Утверждение".
Утверждение, которое должно выполняется всегда.
MC>Это как раз то чем занимаеться дизаин по контракту.
Выполнение таких утверждений — часть контракта.
MC>А тот в свою очередь обязателен в любом релизе.
Обязательно выполнение контракта => обязательно выполнение таких утверждений(а не наличие assert'ов).
Здравствуйте, Юрий Жмеренецкий, Вы писали:
ЮЖ>Здравствуйте, Mike Chaliy, Вы писали:
ЮЖ>... MC>>Я видел схемы именования дизана по контракту ввиде Ассерт.АргументНотНулл(). Ассерт же не переводится как "проверка для дебаггера", Ассерт перводиться как "Утверждение". ЮЖ>Утверждение, которое должно выполняется всегда.
MC>>Это как раз то чем занимаеться дизаин по контракту. ЮЖ>Выполнение таких утверждений — часть контракта.
MC>>А тот в свою очередь обязателен в любом релизе. ЮЖ>Обязательно выполнение контракта => обязательно выполнение таких утверждений(а не наличие assert'ов).
Здравствуйте, Mike Chaliy, Вы писали:
... MC>Нифига не понял...
Предикаты, которые проверяет ассерт, в Desing by Contract являются способом выражения некоторых аспектов контракта. Контракт при этом должы соблюдать как клиент так и поставщик [какой-либо еденицы функциональности]. Разделение на клиента и поставщика необходимо для того, чтобы однозначно идентифицировать место нарушения(баг) контракта и виновника, т.е. ответственного за исправление(безусловное) этого бага.
Одиним из аспектов контракта являются предусловия — условия, которые обязан выполнить клиент, и если клиент их обеспечил, то поставщик гарантированно(здесь есть оговорки) выполнит свою работу ( = выполнит постусловия).
В этом месте люди делятся на два лагеря:
Для одних предусловия — это то что написано выше, для других — то же самое, но с одной оговоркой: 'клиенты иногда могут не выполнять предусловия'. Эта оговорка привоит к тому что поставщики кидают исключения при нарушениях предусловий клиентами.
Одно слово 'иногда' порождает очень существенные различия: у первых контракт выполняется всегда, а у других — иногда. В этом месте второй лагерь не верит первому про 'всегда', а первый... отключает ассерты в релизе . Им они незачем — выполнение контракта они обеспечили.
Здравствуйте, Юрий Жмеренецкий, Вы писали:
ЮЖ>Одно слово 'иногда' порождает очень существенные различия: у первых контракт выполняется всегда, а у других — иногда. В этом месте второй лагерь не верит первому про 'всегда', а первый... отключает ассерты в релизе . Им они незачем — выполнение контракта они обеспечили.
ЮЖ>Вот примерно так.
Клево написано.
А как технически реализована гарнтия того что клиент выполняет условия? И почему ассерты отрубаються в поставщике, а не в клиенте?