Как обозначить атрибут, значение которого вычисляется в момент создания объекта(Прочитано 31238 раз)
Вам надо время от времени итожить свою дискуссию, чтобы сохранить ее понимание для окружающих :)
Попробую. ИМХО:
  • Значение по умолчанию (задается в виде "=defaultValue" или "init: defaultValue") означает каким будет значение property в момент КОНЦА, а не НАЧАЛА инициализации экземпляра объекта. Если есть 2 способа для обозначения одного и того же, может один из них использовать для чего-то другого (например для значения property в момент НАЧАЛА)?
  • Во многих источниках (включая сам стандарт UML!) если property является derived, то и readOnly. Это, как правило, ошибка (сама ситуация допустима, означает, что исходные данные могут меняться только так, что значение derived property остаётся неизменным). Причина:
    Цитата: [прилетело НЛО и...
    link=topic=6740.msg41789#msg41789 date=1495545952] путают "не может меняться извне" с "не может меняться после инициализации".
  • Если property и derived, и имеет defaultValue возникает неоднозначность. Стандарт (OCL 7.3.7) это видит:
    Цитировать
    "Initial and derivation expressions may be mixed together after one context. ... The derivation constraint must be satisfied at any time, hence the derivation includes the initialization. Both are allowed on the same property but they must not be contradictory."
    , но вместо исключения ситуации предлагает "примирение" с ней.



Как возможные промежуточные итоги:
Описание метамодели UML и метамодели OCL можно рассматривать как источник для установления прагматики некоторых конструкций UML.
В стандарте UML есть ошибки.
Утверждение, что инвариант истинен всегда следует принимать без фанатизма. Если в модели есть динамика, то в ней обычно допустимы короткие отрезки времени, в течение которых целостности нет.
[...и улетело НЛО.]



Спасибо.



Когда начинал тему, проблема была довольно абстрактной, на уровне "хорошо бы". А сегодня на работе реальный вопрос: создаем заявку, а в ней 2 атрибута: дата создания и дата встречи. "Дата создания" должна получить в качестве значения текущую дату и больше не меняться. "Дата встречи" должна получить в качестве значения то что укажет пользователь при создании заявки и больше не меняться. А пользователь в качестве предлагаемого значения "даты встречи" получает текущую дату. Что для этой ситуации максимально можно сделать в рамках UML+OCL?



Вдруг уже не актуально, тогда простите.
На диаграмме классов описываем

Заявка
--------------------------------
датаСоздания : Дата = сегодня() {readOnly}
датаВстречи : Дата {readOnly}
--------------------------------
<<create>> новаяЗаявка(дВ : Дата) : Заявка

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

на ocl пишем:
context Заявка::датаСоздания : Дата
init: сегодня()
Это мы продублировали кусок uml-ного описания атрибута, чтоб определение работало в два раза сильнее.))

context Заявка::новаяЗаявка(дВ : Дата) : Заявка
post: result.датаВстречи = дВ
это мы рассказали о том, что датаВстречи заполняется конструктором, параметром которого является значение даты.

Если есть желание утроить, то можно ещё
context Заявка::новаяЗаявка(дВ : Дата) : Заявка
post: result.Создания = сегодня()

Понятно, что дублирование ограничений делает модель менее модифицируемой. Вместо того, чтобы править в одном месте, правим в 2-3 местах.
[...и улетело НЛО.]



Вдруг уже не актуально, тогда простите.
Актуально, появились новые проблемы, но о них в конце.
На диаграмме классов описываем

Заявка
--------------------------------
датаСоздания : Дата = сегодня() {readOnly}
датаВстречи : Дата {readOnly}
--------------------------------
<<create>> новаяЗаявка(дВ : Дата) : Заявка

к этой операции приякориваем примечание о том, что пользователю в формочке для создания заявок предложат по умолчанию, и о том, что из формочки будет вызван этот конструктор.
Получается операция нужна только для того, чтобы к ней "приякорить" примечание - если в примечании указать, что оно относится к созданию Заявки (любому, не обязательно связанному с конкретной операцией), то этого достаточно. Примечание в этом случае будет связано не с конкретной операцией, а с <<create>> для Заявки.
на ocl пишем:
context Заявка::датаСоздания : Дата
init: сегодня()
Это мы продублировали кусок uml-ного описания атрибута, чтоб определение работало в два раза сильнее.))
Дублирование - это плохо, а почему - Вы и сами знаете:
Понятно, что дублирование ограничений делает модель менее модифицируемой. Вместо того, чтобы править в одном месте, правим в 2-3 местах.

context Заявка::новаяЗаявка(дВ : Дата) : Заявка
post: result.датаВстречи = дВ
это мы рассказали о том, что датаВстречи заполняется конструктором, параметром которого является значение даты.
Да, при наличии операции надо объяснить её работу. И ещё где-то надо рассказать, про начальное значение, равное сегодня().
Если есть желание утроить, то можно ещё
context Заявка::новаяЗаявка(дВ : Дата) : Заявка
post: result.Создания = сегодня()
Строго говоря, это не "чистое" утроение - мы говорим, что операция новаяЗаявка подчиняется общим правилам в отношении датыСоздания, то есть утроение только для операции новаяЗаявка. Если будет еще одна операция с <<create>>, то для неё утроение будет, если и в ней написать "post: result.Создания = сегодня()".

Теперь о новой проблеме. Пусть есть Склад, который связан не с одним, а со МНОГИМИ Кладовщиками. Накладная связана с одним Складом и с одним Кладовщиком (причем ни Склад, ни Кладовщик со временем не меняются {readOnly}). В момент создания Накладной её Кладовщик должен быть одним из Кладовщиков, связанных с её Складом, но это только в момент создания. Получается нужен init не типа derive (правило для вычисления значения), а типа invariant (правила проверки того, что значение является подходящим). Как init он действует только при создании, а как invariant не вычисляет, а проверяет.



Получается операция нужна только для того, чтобы к ней "приякорить" примечание - если в примечании указать, что оно относится к созданию Заявки (любому, не обязательно связанному с конкретной операцией), то этого достаточно.
Согласно текущей версии модели других способов создания заявок пока нет. Т. е. операция нужна, чтобы описать, как [вообще] создаются Заявки. К слову, в этом месте стандарт не декларирует, а рекомендует, т. е. указывает некоторую сложившуюся практику.

Примечание в этом случае будет связано не с конкретной операцией, а с <<create>> для Заявки.
С конкретной операцией (как элементом модели) будет связан конкретный экземпляр стереотипа <<create>>. С ним, в свою очередь, -- ограничение. Задать ограничение внутри класса для всех его конструкторов проблематично. Если мы не имеем в виду init:, конечно. Можно дать define-ом общее ограничение, но в каждом постусловии конструктора придётся явно сослаться на него.

Дублирование - это плохо
Ну, это был повод дать повод поговорить о некоторой избыточности OCL-я, который не только дополняет UML, но и в чём-то дублирует.
Прибавлю,  что если модель где-то стабильна, то снизить её модифицируемость в этом месте можно.
 
Теперь о новой проблеме. Пусть есть Склад, который связан не с одним, а со МНОГИМИ Кладовщиками. Накладная связана с одним Складом и с одним Кладовщиком (причем ни Склад, ни Кладовщик со временем не меняются {readOnly}). В момент создания Накладной её Кладовщик должен быть одним из Кладовщиков, связанных с её Складом, но это только в момент создания. Получается нужен init не типа derive (правило для вычисления значения), а типа invariant (правила проверки того, что значение является подходящим). Как init он действует только при создании, а как invariant не вычисляет, а проверяет.
Я всё ещё верю, что readOnly выводимые значения вычисляются [только] в момент создания объекта. Т. е. derive не "распространяет" readOnly дальше -- на элементы вычисляемого выражения.
[...и улетело НЛО.]



Согласно текущей версии модели других способов создания заявок пока нет. Т. е. операция нужна, чтобы описать, как [вообще] создаются Заявки. К слову, в этом месте стандарт не декларирует, а рекомендует, т. е. указывает некоторую сложившуюся практику.
С конкретной операцией (как элементом модели) будет связан конкретный экземпляр стереотипа <<create>>. С ним, в свою очередь, -- ограничение. Задать ограничение внутри класса для всех его конструкторов проблематично. Если мы не имеем в виду init:, конечно. Можно дать define-ом общее ограничение, но в каждом постусловии конструктора придётся явно сослаться на него.
Вот-вот, если про создание сказать нечего, кроме того, что есть правило расчета значения атрибута создаваемого объекта - можно и не создавать операцию со стереотипом <<create>>.
Ну, это был повод дать повод поговорить о некоторой избыточности OCL-я, который не только дополняет UML, но и в чём-то дублирует.
Прибавлю,  что если модель где-то стабильна, то снизить её модифицируемость в этом месте можно.
Мне избыточность (в виде возможности выразить одно и то же несколькими способами) тоже не очень нравится, хочется её приспособить для чего-нибудь нужного, например:
Значение по умолчанию (задается в виде "=defaultValue" или "init: defaultValue") означает каким будет значение property в момент КОНЦА, а не НАЧАЛА инициализации экземпляра объекта. Если есть 2 способа для обозначения одного и того же, может один из них использовать для чего-то другого (например для значения property в момент НАЧАЛА)?
Или для решения свежей проблемы:
Теперь о новой проблеме. Пусть есть Склад, который связан не с одним, а со МНОГИМИ Кладовщиками. Накладная связана с одним Складом и с одним Кладовщиком (причем ни Склад, ни Кладовщик со временем не меняются {readOnly}). В момент создания Накладной её Кладовщик должен быть одним из Кладовщиков, связанных с её Складом, но это только в момент создания. Получается нужен init не типа derive (правило для вычисления значения), а типа invariant (правила проверки того, что значение является подходящим). Как init он действует только при создании, а как invariant не вычисляет, а проверяет.
можно было бы считать, что init не задает выражение, которое рассчитывает значение, а является логическим выражением, которое должно быть истинным в момент создания: для описанного примера context накладная init: self.склад.кладовщик->includes(self.кладовщик).
Я всё ещё верю, что readOnly выводимые значения вычисляются [только] в момент создания объекта. Т. е. derive не "распространяет" readOnly дальше -- на элементы вычисляемого выражения.
Если так, то того же эффекта можно добиться сочетанием init или = (с выражением как в derive) и readOnly - и вычислим в момент создания объекта, и на элементы вычисляемого выражения никакого влияния.



Вот-вот, если про создание сказать нечего, кроме того, что есть правило расчета значения атрибута создаваемого объекта - можно и не создавать операцию со стереотипом <<create>>.
Авторы стандарта UML дают в описании метамодели UML пример, когда для каждого выводимого атрибута (тут у меня натяжка, т. к. выводимый м. б. <> есть правило расчёта) заводят операцию, поясняющую выводимость.
Почитало про выводимость у Фахрутдинова. Мне понравилось. По его формулировке можно сделать вывод, что если для атрибута есть сеттер, то он выводимый. Ну, или мой переводчик с английского на нлошный глючит.)
 
Мне избыточность (в виде возможности выразить одно и то же несколькими способами) тоже не очень нравится, хочется её приспособить для чего-нибудь нужного
Авторы стандарта uml позаботились о том, чтобы поставить рогатку на этом пути. В 9.5.3 сказано, что uml'ьный init работает только в отсутствие "specific setting for the Property"(???) и в отсутствие ocl'ьного init. Авторы стандарта ocl написали так, как-будто про uml'ьный init слыхом не слыхивали, т. е. подпели.
Плюс к этому, разные части uml'ного стандарта писали разные люди, из-за чего можно испытать много радости, читая, например, в 16.1.1.1, что создание объекта и "creation operation" могут содержать в себе инициализацию, но в то же время есть действие CreateObjectAction, которое только создаёт объект, не выполняет инициализацию атрибутов (которая моделируется отдельными действиями).
Или для решения свежей проблемы:можно было бы считать, что init не задает выражение, которое рассчитывает значение, а является логическим выражением, которое должно быть истинным в момент создания: для описанного примера context накладная init: self.склад.кладовщик->includes(self.кладовщик).Если так, то того же эффекта можно добиться сочетанием init или = (с выражением как в derive) и readOnly - и вычислим в момент создания объекта, и на элементы вычисляемого выражения никакого влияния.
Такая трактовка (лог. выражение, которое должно быть истинным) нестандартна (расходится со стандартом ocl).
[...и улетело НЛО.]



Почитало про выводимость у Фахрутдинова. Мне понравилось. По его формулировке можно сделать вывод, что если для атрибута есть сеттер, то он выводимый. Ну, или мой переводчик с английского на нлошный глючит.)
Дайте, пожалуйста, ссылку.
Авторы стандарта uml позаботились о том, чтобы поставить рогатку на этом пути. В 9.5.3 сказано, что uml'ьный init работает только в отсутствие "specific setting for the Property"(???) и в отсутствие ocl'ьного init. Авторы стандарта ocl написали так, как-будто про uml'ьный init слыхом не слыхивали, т. е. подпели.
Такая трактовка (лог. выражение, которое должно быть истинным) нестандартна (расходится со стандартом ocl).
Я понимаю, что это противоречит текущему (им) стандарту! Пытаюсь найти пути реализации потребности минимально модифицируя стандарт (или его трактовку). Осознаю, что любой, даже самый минимальный, отход от стандарта - опасен (читатель диаграммы поймет строго по стандарту - и будет прав).



Дайте, пожалуйста, ссылку.
https://www.uml-diagrams.org/derived-property.html
Цитировать
Derived property is property which value (or values) is produced or computed from other information...
Избирательному цитированию научилось от местных старожилов, если что.
Так вот, если мы согласимся, что значение параметра сеттера в момент его последнего вызова -- это "other information", то можем смело метить атрибут этого сеттера выводимым.)))
[...и улетело НЛО.]




 

Sitemap 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19