Классы-ассоциации (association classes) позволяют дополнительно определять для ассоциаций атрибуты, операции и другие свойства, как показано на рис. 5.12. Из данной диаграммы видно, что Person (Личность) может принимать участие в нескольких совещаниях (Meeting). При этом необходимо каким-то образом хранить информацию о том, насколько внимательной была данная личность; это можно сделать, добавив к ассоциации атрибут attentiveness (внимательность).
На рис. 5.13 показан другой способ представления данной информации: образование самостоятельного класса Attendance (Присутствие). Обратите внимание, как при этом изменили свои значения кратности.
Какие же преимущества может дать класс-ассоциация в качестве компенсации за необходимость помнить еще один вариант уже описанной нотации? Класс-ассоциация дает возможность определить дополнительное ограничение, согласно которому двум участвующим в ассоциации объектам может соответствовать только один экземпляр класса-ассоциации. Мне кажется, необходимо привести еще один пример.
Посмотрим на две диаграммы, изображенные на рис. 5.14. Форма этих диаграмм практически одинакова. Хотя можно себе представить компанию (Company), играющую различные роли (Role) в одном и том же контракте (Contract), но трудно вообразить личность (Person), имеющую различные уровни компетенции в одном и том же навыке (Skill); действительно, скорее всего, это можно считать ошибкой.
В языке UML допустим только последний вариант. Может существовать только один уровень компетенции для каждой комбинации личности и навыка. Верхняя диаграмма на рис. 5.14 не допускает участия компании более чем в одной роли в одном и том же контракте. Если без этого не обойтись, надо превратить Role в полный класс, как это сделано на рис. 5.13.
Реализация классов-ассоциаций не слишком очевидна. Мой совет: реализовывать класс-ассоциацию так, как будто это обычный класс, но методы, предоставляющие информацию связанным классам, должны принадлежать классу-ассоциации. Поэтому для случая, изображенного на рис, 5.12, я бы представил следующие методы класса Person:
class Person
List getAttendances()
List getMeetings()
Таким образом, клиенты объекта Person могут обнаружить сотрудников на совещании; если им требуются детали, то они могут получить собственно часы работы (Attendance). Если вы так делаете, не забудьте об ограничении, при котором для любой пары объектов Person (Личность) и Meeting (Совещание) может существовать только один объект Attendance (Присутствие).
Часто этот вид конструкции можно встретить там, где речь идет о временных изменениях (см., например, рис. 5.15). Однако я считаю, что создание дополнительных классов или классов-ассоциаций может сделать модель сложной для понимания, а также направить реализацию в неправильное русло.
Если я встречаю временную информацию такого типа, то использую для ассоциации ключевое слово «temporal» (временной) (рис. 5.16). Модель означает, что некоторое время личность может работать только в одной компании. Однако по прошествии времени личность сможет работать в нескольких компаниях. Это предполагает интерфейс, описываемый следующими строками:
class Person …
Company getEmployer(); // определение текущего работодателя
Company getEmployer(Date); // определение работодателя на указанный момент
void changeEinployer(Company newEmployer.Date changeDate);
void leaveEmployer (Date changeDate);
Ключевое слово «temporal» не входит в состав языка UML, но я упомянул его здесь по двум причинам. Во-первых, это понятие часто оказывалось полезным для меня как проектировщика. Во-вторых, это демонстрирует, как можно применять ключевые слова для расширения языка UML. Дополнительную информацию по данному вопросу можно найти на https://maHinfowler.com/ap2/timeNarrative.html