структурное отношение между целым и его частями по ссылке
Отношения в UML
В UML имеются четыре разновидности отношений:
Эти отношения являются базовыми строительными блоками отношений. Они используются при написании моделей.
1. Зависимость — семантическое отношение между двумя предметами, в котором изменение в одном предмете (независимом предмете) может влиять на семантику другого предмета (зависимого предмета). Как показано на рис. 10.13, зависимость изображается в виде пунктирной линии, возможно направленной на независимый предмет и иногда имеющей метку.
Рис. 10.13.Зависимости
2. Ассоциация — структурное отношение, которое описывает набор связей, являющихся соединением между объектами. Агрегация — это специальная разновидность ассоциации, представляющая структурное отношение между целым и его частями. Как показано на рис. 10.14, ассоциация изображается в виде сплошной линии, возможно направленной, иногда имеющей метку и часто включающей другие «украшения», такие как мощность и имена ролей.
Рис. 10.14.Ассоциации
3. Обобщение — отношение специализации/обобщения, в котором объекты специализированного элемента (потомка, ребенка) могут заменять объекты обобщенного элемента (предка, родителя). Иначе говоря, потомок разделяет структуру и поведение родителя. Как показано на рис. 10.15, обобщение изображается в виде сплошной стрелки с полым наконечником, указывающим на родителя.
Рис. 10.15.Обобщения
4. Реализация — семантическое отношение между классификаторами, где один классификатор определяет контракт, который другой классификатор обязуется выполнять (к классификаторам относят классы, интерфейсы, компоненты, элементы Use Case, кооперации). Отношения реализации применяют в двух случаях: между интерфейсами и классами (или компонентами), реализующими их; между элементами Use Case и кооперациями, которые реализуют их. Как показано на рис. 10.16, реализация изображается как нечто среднее между обобщением и зависимостью.
Отношения классов — от UML к коду
Введение
Диаграмма классов UML позволяет обозначать отношения между классами и их экземплярами. Для чего они нужны? Они нужны, например, для моделирования прикладной области. Но как отношения отражаются в программном коде? Данное небольшое исследование пытается ответить на этот вопрос — показать эти отношения в коде.
Сначала попробуем прояснить, как относятся друг к другу отношения между классами в UML. Используя различные источники удалось построить следующую структурную схему, демонстрирующую разновидности отношений:
Рис. 1 — Отношения между классами
Ассоциации имеют навигацию: двунаправленную или однонаправленную, указывающую на направление связи. То есть у каждого вида ассоциации еще есть два подвида, которое на рисунке не показаны.
1. Обобщение
Итак, наша цель — построить UML-диаграмму классов (Class Model), а затем отразить ее в объектно-ориентированном коде.
В качестве прикладной области возьмем отдел кадров некого предприятия и начнем строить его модель. Для примеров будем использовать язык Java.
Отношение обобщения — это наследование. Это отношение хорошо рассматривается в каждом учебнике какому-либо ООП языку. В языке Java имеет явную реализацию через расширение(extends) одного класса другим.
Рис. 2 — Отношение обобщения
Класс «Man»(человек) — более абстрактный, а «Employee»(сотрудник) более специализированный. Класс «Employee» наследует свойства и методы «Man».
Попробуем написать код для этой диаграммы:
2. Ассоциация
2.1 Бинарная
В модель добавили класс «IdCard», представляющий идентификационную карточку(пропуск) сотрудника. Каждому сотруднику может соответствовать только одна идентификационная карточка, мощность связи 1 к 1.
Рис. 3 — Бинарная ассоциация
В теле программы создаем объекты и связываем их:
Класс Employee имеет поле card, у которого тип IdCard, так же класс имеет методы для присваивания значения(setIdCard) этому полю и для
получения значения(getIdCard). Из экземпляра объекта Employee мы можем узнать о связанном с ним объектом типа IdCard, значит
навигация (стрелочка на линии) направлена от Employee к IdCard.
2.2 N-арная ассоциация
Представим, что в организации положено закреплять за работниками помещения. Добавляем новый класс Room.
Каждому объекты работник(Employee) может соответствовать несколько рабочих помещений. Мощность связи один-ко-многим.
Навигация от Employee к Room.
Рис. 4 — N-арная ассоциация
Теперь попробуем отразить это в коде. Новый класс Room:
Добавим в класс Employee поле и методы для работы с Room:
2.3 Агрегация
Введем в модель класс Department(отдел) — наше предприятие структурировано по отделам. В каждом отделе может работать один или более человек. Можно сказать, что отдел включает в себя одного или более сотрудников и таким образом их агрегирует. На предприятии могут быть сотрудники, которые не принадлежат ни одному отделу, например, директор предприятия.
Рис. 5 — Агрегация
Итак, наш класс, помимо конструктора и метода изменения имени отдела, имеет методы для занесения в отдел нового сотрудника, для удаления сотрудника и для получения всех сотрудников входящих в данный отдел. Навигация на диаграмме не показана, значит она является двунаправленной: от объекта типа «Department» можно узнать о сотруднике и от объекта типа «Employee» можно узнать к какому отделу он относится.
Так как нам нужно легко узнавать какому отделу относится какой-либо сотрудник, то добавим в класс Employee поле и методы для назначения и получения отдела.
2.3.1 Композиция
Предположим, что одним из требований к нашей системе является требование о том, чтоб хранить данные о прежней занимаемой должности на предприятии.
Введем новый класс «pastPosition». В него, помимо свойства «имя»(name), введем и свойство «department», которое свяжет его с классом «Department».
Данные о прошлых занимаемых должностях являются частью данных о сотруднике, таким образом между ними связь целое-часть и в то же время, данные о прошлых должностях не могут существовать без объекта типа «Employee». Уничтожение объекта «Employee» должно привести к уничтожению объектов «pastPosition».
Рис. 6 — Композиция
В класс Employee добавим свойства и методы для работы с данными о прошлой должности:
3. Зависимость
Для организации диалога с пользователем введем в систему класс «Menu». Встроим один метод «showEmployees», который показывает список сотрудников и их должности. Параметром для метода является массив объектов «Employee». Таким образом, изменения внесенные в класс «Employee» могут потребовать и изменения класса «Menu».
Рис. 7 — Зависимость
Заметим, что класс «Menu» не относится к прикладной области, а представляет собой «системный» класс воображаемого приложения.
Класс «Menu»:
4. Реализация
Реализация, как и наследование имеет явное выражение в языке Java: объявление интерфейса и возможность его реализации каким-либо классом.
Для демонстрации отношения «реализация» создадим интерфейс «Unit». Если представить, что организация может делиться не только на отделы, а например, на цеха, филиалы и т.д. Интерфейс «Unit» представляет собой самую абстрактную единицу деления. В каждой единице деления работает какое-то количество сотрудников, поэтому метод для получения количества работающих людей будет актуален для каждого класса реализующего интерфейс «Unit».
Рис. 8 — Реализация
Реализация в классе «Department»:
Как видим, реализация метода «getPersonCount» не совсем актуальна для класса «Department», так как он имеет метод «getEmployees», который возвращает
коллекцию объектов «Employee».
Выводы
Язык моделирования UML имеет набор отношений для построения модели классов, но даже такой развитой ООП язык, как Java имеет только две явные конструкции для отражения связей: extends(расширение) и interface/implements(реализация).
В результате моделирования получили следующую диаграмму:
Рис. 8 — Диаграмма классов
Литература
1) Г. Буч, Д. Рамбо, А. Джекобсон. Язык UML Руководство пользователя.
3) Эккель Б. Философия Java. Библиотека программиста. — СПб: Питер, 2001. — 880 с.
4) Орлов С. Технологии разработки программного обеспечения: Учебник. — СПб: Питер, 2002. — 464 с.
5) Мухортов В.В., Рылов В.Ю.Объектно-ориентированное программирование, анализ и дизайн. Методическое пособие. — Новосибирск, 2002.
UML-диаграммы классов
UML – унифицированный язык моделирования (Unified Modeling Language) – это система обозначений, которую можно применять для объектно-ориентированного анализа и проектирования.
Его можно использовать для визуализации, спецификации, конструирования и документирования программных систем.
Словарь UML включает три вида строительных блоков:
Сущности – это абстракции, которые являются основными элементами модели, связи соединяют их между собой, а диаграммы группируют представляющие интерес наборы сущностей.
Диаграмма – это графическое представление набора элементов, чаще всего изображенного в виде связного графа вершин (сущностей) и путей (связей). Язык UML включает 13 видов диаграмм, среди которых на первом месте в списке — диаграмма классов, о которой и пойдет речь.
Диаграммы классов показывают набор классов, интерфейсов, а также их связи. Диаграммы этого вида чаще всего используются для моделирования объектно-ориентированных систем. Они предназначены для статического представления системы.
Большинство элементов UML имеют уникальную и прямую графическую нотацию, которая дает визуальное представление наиболее важных аспектов элемента.
Сущности
Диаграммы классов оперируют тремя видами сущностей UML:
Поведенческие сущности – динамические части моделей UML. Это «глаголы» моделей, представляющие поведение модели во времени и пространстве. Основной из них является взаимодействие – поведение, которое заключается в обмене сообщениями между наборами объектов или ролей в определенном контексте для достижения некоторой цели. Сообщение изображается в виде линии со стрелкой, почти всегда сопровождаемой именем операции.
Структурные сущности — классы
Класс – это описание набора объектов с одинаковыми атрибутами, операциями, связями и семантикой.
Графически класс изображается в виде прямоугольника, разделенного на 3 блока горизонтальными линиями:
Для атрибутов и операций может быть указан один из трех типов видимости:
Видимость для полей и методов указывается в виде левого символа в строке с именем соответствующего элемента.
Каждый класс должен обладать именем, отличающим его от других классов. Имя – это текстовая строка. Имя класса может состоять из любого числа букв, цифр и знаков препинания (за исключением двоеточия и точки) и может записываться в несколько строк.
На практике обычно используются краткие имена классов, взятые из словаря моделируемой системы. Каждое слово в имени класса традиционно пишут с заглавной буквы (верблюжья конвенция), например Sensor (Датчик) или TemperatureSensor (ДатчикТемпературы). 
Для абстрактного класса имя класса записывается курсивом.
Атрибут (свойство) – это именованное свойство класса, описывающее диапазон значений, которые может принимать экземпляр атрибута. Класс может иметь любое число атрибутов или не иметь ни одного. В последнем случае блок атрибутов оставляют пустым.
Атрибут представляет некоторое свойство моделируемой сущности, которым обладают все объекты данного класса. Имя атрибута, как и имя класса, может представлять собой текст. На практике для именования атрибута используются одно или несколько коротких существительных, выражающих некое свойство класса, к которому относится атрибут.
Можно уточнить спецификацию атрибута, указав его тип, кратность (если атрибут представляет собой массив некоторых значений) и начальное значение по умолчанию.
Статические атрибуты класса обозначаются подчеркиванием.
Операция (метод) – это реализация метода класса. Класс может иметь любое число операций либо не иметь ни одной. Часто вызов операции объекта изменяет его атрибуты.
Графически операции представлены в нижнем блоке описания класса.
Допускается указание только имен операций. Имя операции, как и имя класса, должно представлять собой текст. На практике для именования операции используются короткие глагольные конструкции, описывающие некое поведение класса, которому принадлежит операция. Обычно каждое слово в имени операции пишется с заглавной буквы, за исключением первого, например move (переместить) или isEmpty (проверка на пустоту).
Можно специфицировать операцию, устанавливая ее сигнатуру, включающую имя, тип и значение по умолчанию всех параметров, а применительно к функциям – тип возвращаемого значения.
Абстрактные методы класса обозначаются курсивным шрифтом.
Статические методы класса обозначаются подчеркиванием.
Изображая класс, не обязательно показывать сразу все его атрибуты и операции. Для конкретного представления, как правило, существенна только часть атрибутов и операций класса. В силу этих причин допускается упрощенное представление класса, то есть для графического представления выбираются только некоторые из его атрибутов. Если помимо указанных существуют другие атрибуты и операции, вы даете это понять, завершая каждый список многоточием.
Чтобы легче воспринимать длинные списки атрибутов и операций, желательно снабдить префиксом (именем стереотипа) каждую категорию в них. В данном случае стереотип – это слово, заключенное в угловые кавычки, которое указывает то, что за ним следует.
Отношения между классами
Существует четыре типа связей в UML:
Эти связи представляют собой базовые строительные блоки для описания отношений в UML, используемые для разработки хорошо согласованных моделей.
Первая из них – зависимость – семантически представляет собой связь между двумя элементами модели, в которой изменение одного элемента (независимого) может привести к изменению семантики другого элемента (зависимого). Графически представлена пунктирной линией, иногда со стрелкой, направленной к той сущности, от которой зависит еще одна; может быть снабжена меткой.
Ассоциация – это структурная связь между элементами модели, которая описывает набор связей, существующих между объектами.
Ассоциация показывает, что объекты одной сущности (класса) связаны с объектами другой сущности таким образом, что можно перемещаться от объектов одного класса к другому.
Например, класс Человек и класс Школа имеют ассоциацию, так как человек может учиться в школе. Ассоциации можно присвоить имя «учится в». В представлении однонаправленной ассоциации добавляется стрелка, указывающая на направление ассоциации.
Двойные ассоциации представляются линией без стрелок на концах, соединяющей два классовых блока.
Ассоциация может быть именованной, и тогда на концах представляющей её линии будут подписаны роли, принадлежности, индикаторы, мультипликаторы, видимости или другие свойства.
Пример кода и диаграммы классов для него
Программа получает данные с датчика температуры (вводятся с консоли) — по 5 измерений для каждого из двух объектов класса TemperatureMeasure и усредняет их. Также предусмотрен класс ShowMeasure для вывода измеренных значений.
Структурное отношение между целым и его частями по ссылке
Сущности на диаграммах классов связываются главным образом отношениями ассоциации (в том числе агрегирования и композиции) и обобщения. Отношения зависимости и реализации на диаграммах классов применяются реже, но, тем не менее, они также применяются, и мы начнем с них, как с более простых.
3.3.1. Отношения зависимости и реализации
Всего в UML определено довольно большое количество стандартных стереотипов отношения зависимости, которые можно разделить на несколько групп:
Здесь рассматриваются зависимости первой группы, которые перечислены в следующей таблице.
Табл. Стандартные стереотипы зависимостей на диаграмме классов
| Стереотип | Описание |
|---|---|
| «bind» | Подстановка параметров в шаблон. Независимой сущностью является шаблон (класс с параметрами), а зависимой ‒ класс, который получается из шаблона заданием аргументов. |
| «call» | Указывает зависимость между двумя операциями: операция зависимого класса вызывает операцию независимого класса. |
| «derive» | Буквально означает «может быть вычислен по». Зависимость с данным стереотипом применяется не только к классам, но и к другим элементам модели: атрибутам, ассоциациям и т.д. Суть состоит в том, зависимый элемент может быть восстановлен по информации, содержащейся в независимом элементе. Таким образом, данная зависимость показывает, что зависимый элемент, вообще говоря, излишен и введен в модель из соображений удобства, наглядности и т.д. |
| «friend» | Назначает специальные права видимости. Зависимый класс имеет доступ к составляющим независимого класса, даже если по общим правилам видимости такие права у него отсутствуют. |
| «instanceOf» | Указывает, что зависимый объект (или класс) является экземпляром независимого класса (метакласса). |
| «instantiate» | Указывает, что операции зависимого класса создают экземпляры независимого класса. |
| «powertype» | Показывает, что экземплярами зависимого класса являются подклассы независимого класса. Таким образом, в данном случае зависимый класс является метаклассом. |
| «refine» | Указывает, что зависимый класс уточняет (конкретизирует) независимый. Данная зависимость показывает, что связанные классы концептуально совпадают, но находятся на разных уровнях абстракции. |
| «use» | Зависимость самого общего вида, показывающая, что зависимый класс каким-либо образом использует независимый класс. |
Повторим еще раз, что зависимости на диаграммах классов используются сравнительно редко, потому что имеют более расплывчатую семантику по сравнению с ассоциациями и обобщением.
Рассмотрим отношение реализации. Между интерфейсами и другими классификаторами, в частности, классами, на диаграмме классов применяются два отношения:
Никаких ограничений на использование отношения реализации не накладывается: класс может реализовывать много интерфейсов, и наоборот, интерфейс может быть реализован многими классами. Нет ограничений и на использование зависимостей со стереотипом «call» ‒ класс может вызывать любые операции любых видимых интерфейсов. Семантика зависимости со стереотипом «call» очень проста ‒ эта зависимость указывает, что в операциях класса, находящегося на независимом полюсе, вызываются операции класса (в частности, интерфейса) находящегося на зависимом полюсе.
Рис. Отношения реализации и использования интерфейсов
Используя нотацию «чупа-чупс», появившуюся в UML 2, эту же модель можно изобразить лаконично, симметрично и просто, как показано ниже.
Рис. Использование нотации «чупа-чупс»
3.3.2. Отношение обобщения
Отношение обобщения (параграф 1.4.2) часто применяется на диаграмме классов. Действительно, трудно представить себе ситуацию, когда между классами в одной системе нет ничего общего. Как правило, общее есть, и это общее целесообразно выделить в отдельный класс. При этом общие составляющие, собранные в суперклассе, автоматически наследуются подклассами.
При обобщении выполняется принцип подстановки, что фактически означает увеличение гибкости и универсальности программного кода, при одновременном сохранении надежности, обеспечиваемой контролем типов. Действительно, если, например, в качестве типа параметра некоторой процедуры указать суперкласс, то процедура будет с равным успехом работать в случае, когда в качестве аргумента ей передан объект любого подкласса данного суперкласса. Суперкласс может быть конкретным, идентифицированным одним из методов, описанных в параграфе 3.1.4, а может быть абстрактным, введенным именно для построения отношений обобщения.
Принцип подстановки, сформулированный Барбарой Лисков (Liskov substitution principle) заключается в том, что экземпляр подкласса может использоваться везде, где используется экземпляр его суперкласса, поддерживая таким образом контракт, предлагаемый суперклассом. Другими словами, подкласс обеспечивает тот же интерфейс что и суперкласс, т.е. происходит открытое наследование интерфейса.
Рис. Отношение обобщения
В объектно-ориентированном программировании распространена идиома, согласно которой корневые интерфейсы (классы) иерархий определяют абстрактную операцию, переопределяемую в подклассах, которая во время исполнения возвращает некоторую информацию о конкретном экземпляре созданного подкласса. Данную информацию можно использовать, например, для тестирования или в операциях безопасного приведения типов.
Отношения обобщения можно задавать в UML довольно свободно, но не совсем произвольно. Обобщения в модели должны образовывать строгий частичный порядок.
Таким образом, в UML допускается, чтобы класс был подклассом нескольких суперклассов (множественное наследование), не требуется, чтобы у базовых классов был общий суперкласс (несколько иерархий обобщения) и вообще не накладывается никаких ограничений, кроме частичной упорядоченности (т.е. отсутствия циклов в цепочках обобщений). Нарушение данного условия является синтаксической ошибкой, однако не все инструменты проверяют это условие ‒ цикл может быть незаметен, потому что отдельные дуги цикла обобщений могут быть показаны на разных диаграммах.
При множественном наследовании (multiple inheritance) возможны конфликты: суперклассы содержат составляющие, которые невозможно включить в один подкласс, например, атрибуты с одинаковыми именами, но разными типами. В UML конфликты при множественном наследовании считаются нарушением правил непротиворечивости модели (параграф 1.8.2). Если же конфликты отсутствуют, то множественное наследование в UML не только не запрещается, но даже поощряется! В частности, метамодели в стандарте изобилуют примерами множественного наследования.
Несмотря на те сложности, которые существуют при использовании и в реализации поддержки множественного наследования, его не стоит бояться. Как заметил один из авторов UML (Гради Буч) «проблема множественного наследования находится в головах у архитекторов, а не в языке моделирования».
В UML существует возможность выделять в множестве обобщений подмножества обобщений (generalization set) и задавать ограничения для них.
Рис. Подмножества обобщений
∇ Владелец компании (employer) может быть и работником компании (employee).
Табл. Ограничения на подмножество обобщений
| Ограничение | Применение |
|---|---|
| полнота | Множество обобщений, входящих в подмножество, является полным, т.е. определяет все возможные подтипы для данной характеристики суперклассификатора. Каждый экземпляр суперклассификатора должен быть экземпляром какого-либо подклассификатора. |
| неполнота | Множество обобщений, входящих в подмножество, не является полным, т.е. определяет только часть возможных подклассификаторов для данной характеристики суперклассификатора. Некоторый экземпляр суперклассификатора может не являться экземпляром ни одного подклассификатора из множества. |
| несовместность | Области значений подклассификаторов, входящих в данное подмножество не пересекаются, т.е. являются взаимоисключающими. У них не может быть общего прямого или косвенного экземпляра. |
| совместность | Области значений подклассификаторов могут пересекаться, т.е. они не являются взаимоисключающими. У них может быть общий прямой или косвенный экземпляр. |
Как видно из описания, приведенного выше, пары
Перед тем как приступить к описанию наиболее часто используемого отношения ‒ отношения ассоциации, подведем итог сказанному с помощью диаграммы метамодели отношений зависимости, реализации и обобщения.
Рис. Метамодель отношений зависимости, реализации и обобщения
3.3.3. Ассоциации и их дополнения
Отношение ассоциации является, видимо, самым важным на диаграмме классов. В общем случае ассоциация, нотация которой ‒ сплошная линия, соединяющая классы, означает, что экземпляры одного класса связаны с экземплярами другого класса. Поскольку экземпляров может быть много, и каждый может быть связан с несколькими, ясно, что ассоциация является дескриптором, который описывает множество наборов связанных объектов. В UML ассоциация является классификатором, экземпляры которого называются связями.
Связь (link) ‒ это экземпляр ассоциации (или соединителя), который представляет собой упорядоченный набор (кортеж, tuple) ссылок на экземпляры классификаторов на полюсах ассоциации.
Связь между объектами (экземплярами классов) в программе может быть организована самыми разными способами. Например, в объекте одного класса может храниться указатель или ссылка на объект другого класса. Связь не обязательно является непосредственно хранимым физическим адресом. Этот адрес может динамически вычисляться во время выполнения программы на основании другой информации. Например, если объекты представлены как записи в таблице базы данных, то связь означает, что в записи одного объекта имеется поле, значением которого является первичный ключ записи другого объекта (из другой таблицы). Еще пример: использование какого-либо механизма динамического связывания по имени (уникальному идентификатору) объекта. При моделировании на UML техника реализации связи между объектами не имеет значения. Ассоциация в UML подразумевает лишь то, что связанные объекты обладают достаточной информацией для организации взаимодействия. Возможность взаимодействия означает, что объект одного класса может послать сообщение объекту другого класса, в частности, вызвать метод или же прочитать или изменить значение открытого атрибута. Поскольку в объектно-ориентированной программе такого рода действия и составляют суть выполнения программы, моделирование структуры взаимосвязей классов (т.е. выявление ассоциаций) является одной из ключевых задач моделирования.
Как уже было сказано, базовая нотация ассоциации (сплошная линия) позволяет указать, что объекты ассоциированных классов могут взаимодействовать во время выполнения. Но это только малая часть того, что можно моделировать с помощью отношения ассоциации. Для ассоциации в UML предусмотрено наибольшее количество различных дополнений, которые мы сначала перечислим, а потом рассмотрим по порядку. Дополнения, как обычно, не являются обязательными: их используют при необходимости, в различных ситуациях по-разному. Если использовать все дополнения сразу, то диаграмма становится настолько перегруженной, что ее трудно читать. Итак, для ассоциации определены следующие дополнения:
∇ Напомним, что полюсом называется конец линии ассоциации. Обычно используются двухполюсные (бинарные) ассоциации, но могут быть и многополюсные.
Рассмотрим их по порядку.
3.3.4. Имя ассоциации. Кратность полюса ассоциации
Имя ассоциации указывается в виде строки текста над (или под, или рядом с) линией ассоциации. Имя не несет дополнительной семантической нагрузки, а просто позволяет различать ассоциации в модели. Обычно имя указывают в случаях многополюсных ассоциаций или, когда одна и та же группа классов связана несколькими различными ассоциациями. Однако строгого правила на этот счет нет.
Рис. Имя ассоциации и направление чтения
Рис. Кратность полюсов ассоциации
Более сложные случаи также легко моделируются с помощью кратности полюсов. Например, если мы хотим предусмотреть совмещение должностей и хранить информацию даже о неработающих сотрудниках, то диаграмма примет вид, приведенный ниже (запись * эквивалентна записи 0..* ).
Рис. Использование неопределенной кратности
3.3.5. Агрегация и композиция
В UML используются два частных, но очень важных случая отношения ассоциации, которые называются агрегацией и композицией. В обоих случаях речь идет о моделировании отношения типа «часть ‒ целое». Ясно, что отношения такого типа следует отнести к отношениям ассоциации, поскольку части и целое обычно взаимодействуют.
Рис. Отношение агрегации
При этом никаких дополнительных ограничений не накладывается: экземпляр класса Person (часть) может быть связан с другими объектами (т.е. класс Person может участвовать в нескольких агрегациях), создаваться и уничтожаться независимо от экземпляров класса Workgroup (целого).
Однако часть может быть отделена от целого до того, как оно будет удалено. В указанном случае композиция будет разрушена.
∇ Альтернативный способ выражения отношений агрегации и композиции описан в параграфе 3.5.1
Рис. Отношение композиции
При использовании отношения композиция, «целое» часто называют композитом, а при использовании агрегации ‒ агрегатом.
∇ Некоторые инструменты автоматически помещают на диаграмму классов соответствующую линию композиции или агрегации при определении таких атрибутов.
В комбинации с указанием кратности, отношения ассоциации, агрегации и композиции позволяют лаконично и полно отобразить структуру классов: что из чего состоит и как связано. Здесь приведен пример одного из вариантов такой структуры для информационной системы отдела кадров.
Рис. Структура связей классов информационной системы отдела кадров
Обратите внимание на ассоциацию с именем works for 1 на рисунке выше. Это производная ассоциация.
Производный элемент (derived element) ‒ это элемент, который можно вычислить или определить по другим элементам.
Сделаем еще два важных замечания относительно композиции.
Простейшим и, как следствие, не самым лучшим, является решение, представленное ниже.
Рис. Пример использования атрибутов
∇ Более подробно об ассоциации, которая связывает класс с самим собой, мы поговорим в следующем параграфе.
Рис. Пример использования композиции
Второе замечание связано с определением композиции, которое приведено выше. Из него следует, что композит управляет временем жизни частей. Таким образом, речь идет об экземплярах классов, которые являются частями для данного экземпляра композита.
Рис. Первый вариант реализации сложной композиции
Второй вариант решения состоит в следующем: если у группы классов есть нечто общее, то можно завести абстрактный суперкласс (класс UnitWithBoss ) и установить требуемую композицию с ним.
Рис. Второй вариант реализации сложной композиции
∇ Напомним, что ассоциация (и, в частности, композиция) является классификатором, экземплярами которого являются связи, а значит, может участвовать в отношении обобщения.
Рис. Третий вариант реализации сложной композиции
Рис. Использование redefines для полюсов ассоциации
3.3.6. Роль полюса ассоциации. Многополюсная ассоциация
Поскольку мы разобрались с интерфейсами, их реализацией и использованием, нам представляется уместным еще раз повторить определение понятия, которое несколько раз уже встречалось.
Роль (role) ‒ это интерфейс, который предоставляет классификатор в данной ассоциации.
Мы надеемся, что туман неясности по поводу понятия «роль» в UML, порожденный нашими постоянными, но неизбежными забеганиями вперед, теперь полностью развеялся.
Напомним, что полюс ассоциации ‒ это точка соприкосновения линии ассоциации с прямоугольником класса. Именно вблизи этой точки располагаются многочисленные дополнения полюсов ассоциации.
Роль полюса ассоциации (association end role), называемая также спецификатором интерфейса ‒ это способ указать, как именно участвует классификатор (присоединенный к данному полюсу ассоциации) в ассоциации.
Нотация этого дополнения ‒ текст, указанный на полюсе ассоциации. В общем случае роль полюса ассоциации имеет следующий синтаксис:
Имя является обязательным, оно называется именем роли и фактически является собственным именем полюса ассоциации, позволяющим различать полюса. Если рассматривается одна ассоциация, соединяющая два различных класса, то в именах ролей нет нужды: полюса ассоциации легко можно различить по именам классов, к которым они присоединены. Однако, если это не так, т.е. если два класса соединены несколькими ассоциациями, или же если ассоциация соединяет класс с самим собой, то указание ролей полюсов ассоциации является необходимым. Такая ситуация отнюдь не является надуманной, как может показаться и уже была продемонстрирована выше. Приведем еще один пример.
Рис. Описание иерархии должностей
Рис. Роли полюсов ассоциации
Имея в виду ту же самую цель, можно поступить и по-другому: непосредственно включить в описание класса составляющие, ответственные за обеспечение нужной функциональности (следующий рисунок). Однако такое решение не самое лучшее: во-первых, оно слишком привязано к реализации, разработчику не оставлено никакой свободы для творческих поисков эффективного решения; во-вторых оно менее наглядно, потеряны информативные имена ролей и ассоциации; в-третьих, оно менее надежно ‒ в модели выше подчиненный синтаксически не может потребовать отчета от начальника, а в модели ниже ‒ может, и нужно предусматривать дополнительные средства для обработки этой ошибки.
Рис. Атрибуты и операции, обеспечивающие реализацию ролей
Рис. Вариант использования направлений навигации
В случае если ни один из элементов нотации описывающих свойство возможность навигации не присутствует на конце линии ассоциации, то никакого предположения о значении этого свойства для данного полюса сделать нельзя. Значение по умолчанию для него в UML отсутствует. В качестве примера можно рассмотреть диаграмму Описание иерархии должностей.
Рис. Описание иерархии должностей
Если ее не уточнить как показано ниже, то всевозможные предположения о значении данного свойства могут иметь место.
Рис. Вариант использования направлений навигации
Реальная практика использования свойства возможности навигации показывает, что помимо явной интерпретации (описанной в стандарте), существует и неявная (часто используемая инструментами и подразумеваемая в литературе), а именно, считается, что отсутствие стрелочек на концах линии ассоциации говорит о возможности навигации.
Оставшуюся часть параграфа мы посвятим обсуждению многополюсных ассоциаций.
∇ Речь идет о матричной структуре управления.
Рис. Многополюсная ассоциация
Чтобы полностью оценить полезность многополюсных ассоциаций, мы советуем читателям проделать весьма поучительное упражнение: попытаться построить модель, семантически эквивалентную только что приведенной, но без использования многополюсных ассоциаций (подсказка: придется ввести дополнительные сущности и отношения).
3.3.7. Видимость полюса ассоциации. Свойства упорядоченности и уникальности
Рис. Фрагмент диаграммы классов ИС ОК
Другими словами, использование маршрута Person ‒ Company ‒ Department противоречит техническому заданию. Действительно, из приведенного фрагмента модели следует, что потенциально любой работник может получить полную информацию о структуре всего предприятия, на котором он работает.
Рассмотрим маршрут Person ‒ Company ‒ Department еще раз. Каждый их двух участков этого маршрута имеет право на существование. Действительно, работнику не возбраняется знать, на кого он работает (ассоциация между Person и Company ), а компания должна иметь информацию о своей структуре (композиция между Company и Department ). Получается, что навигация по отдельным участкам маршрута Person ‒ Company ‒ Department является допустимой, в то время как на прохождение всего маршрута должно быть наложено ограничение, а точнее говоря, запрет.
Выходом из подобных (не часто, но все-таки иногда возникающих) ситуаций служит использование видимости полюса ассоциации.
Видимость полюса ассоциации ‒ это указание того, является ли классификатор, присоединенный к данному полюсу ассоциации, видимым для классификаторов (кроме непосредственно присоединенных), маршруты из которых ведут к нему.
Пожалуйста, не думайте, что мы хотим вас запутать с помощью подобных формальных определений. На самом деле все очень просто. Продолжим рассмотрение предыдущего примера.
Рис. Пример использования свойства видимости на полюсе ассоциации
Как видно из рассмотренного примера, видимость на полюсе ассоциации может применяться там, где нужна более «точная настройка» чем та, которую обеспечивает свойство возможность навигации (см. параграф 3.3.6).
Закончив с этим дополнением, перейдем к следующим.
Описываемые ниже дополнения встречаются сравнительно редко, но иногда без них не обойтись. Они не имеют специальной графической нотации, а записываются в виде стандартных ограничений на полюсах ассоциации. Все их условно можно разделить на две группы, которые мы рассмотрим в этом параграфе.
Ограничения первой группы связаны с понятиями упорядоченности объектов на полюсе ассоциации (ordering) и уникальности (uniqueness), т.е. наличием (отсутствием) в этом множестве одинаковых объектов.
Данные ограничения можно свести в следующую таблицу.
Табл. Свойства упорядоченности и уникальности
| Упорядоченное множество | Неупорядоченное множество | |
|---|---|---|
| Есть одинаковые элементы | последовательность | мультимножество |
| Нет одинаковых элементов | упорядоченное множество | множество |
Обычно считается, что множество объектов на полюсе связи может изменяться произвольным образом (в пределах специфицированной кратности). Например, в один момент работы информационной системы отдела кадров в данном подразделении может быть 10 одних должностей, а в другой ‒ 20 других. Совершенно аналогично, значение атрибута обычно может произвольным образом меняться в процессе жизни объекта (в пределах указанного типа атрибута). Однако иногда необходимо определенным образом ограничить изменяемость атрибута (см. табл. Значения свойства изменяемости атрибутав параграф 3.2.2). Аналогично иногда нужно ограничить изменяемость состава множества объектов присоединенных к полюсу связи (экземпляру ассоциации). Для этого применяется тот же самый набор стандартных значений (еще раз см. табл. Значения свойства изменяемости атрибута).
Рис. Упорядоченность и изменяемость множества объектов на полюсе связи
Вторая группа ограничений появилась только в UML 2 и позволяет манипулировать множествами объектов на полюсах.
Рис. Иллюстрация к задаче о восьмиугольнике
Алгоритм решения задачи ‒ построение выпуклой оболочки. Возможная структура данных для решения поставленной задачи описывается следующей диаграммой классов.
Рис. Использование ограничения subsets
На приведенной диаграмме экземпляры класса Polygon хранят исходный массив точек, а экземпляры класса Octagon упорядоченный набор вершин, получаемый после построения выпуклой оболочки, которым и определяются. Ограничение
Рис. Использование ограничения union
3.3.8. Класс ассоциации и квалификатор
В процессе проектирования возможны ситуации, когда ассоциация должна иметь собственные атрибуты (и даже операции), значения которых хранятся в экземплярах ассоциации ‒ связях. В таком случае применяется специальный элемент моделирования ‒ класс ассоциации.
Класс ассоциации (association class) ‒ это сущность, которая является ассоциацией, но также имеет в своем составе составляющие класса.
Класс ассоциации изображается в виде символа класса, присоединенного пунктирной линией к линии ассоциации.
Вернемся к информационной системе отдела кадров.
Рис. Класс ассоциации
Может показаться, что понятие класса ассоциации в UML является надуманным и излишним. Действительно, можно применить стандартный прием нормализации, который часто используется при проектировании схем баз данных: систематическим образом избавиться от отношений «многие ко многим» путем введения дополнительной сущности 1 и трех отношений «один ко многим». Применительно к данному примеру такой прием дает решение, приведенное на следующем рисунке.
Рис. Элиминация отношения «многие ко многим» с помощью введения дополнительной сущности
Для того чтобы решить задачу поиска конкретного сотрудника (конкретный экземпляр класса Person ) всегда можно применить следующее тривиальное решение: хранить ключ (ИНН) в самом объекте класса Person в качестве атрибута и, получив множество объектов, перебирать их все последовательно до тех пор, пока не найдется тот, который имеет искомое значение ключа. Такой прием называется линейным поиском. Для компаний, в которых не очень много сотрудников, данный способ может быть вполне приемлемым. Но в других случаях, когда к полюсу с кратностью «много» присоединено действительно много объектов, линейный поиск слишком неэффективен. Известно множество структур данных, позволяющих эффективно выделить (найти в множестве) объект по ключу: сортированные массивы, таблицы расстановки (хэш-таблицы), деревья сортировки, внешние индексы и др. Эти приемы обобщены в UML понятием квалификатора.
Квалификатор полюса ассоциации (qualifier) ‒ это атрибут (или несколько атрибутов) полюса ассоциации, значение которого (которых) позволяет выделить один (или несколько) объектов класса, присоединенного к другому полюсу ассоциации.
Квалификатор изображается в виде небольшого прямоугольника на полюсе ассоциации, примыкающего к прямоугольнику класса. Внутри этого прямоугольника (или рядом с ним) указываются имена и, возможно, типы атрибутов квалификатора. Описание квалифицирующего атрибута ассоциации имеет такой же синтаксис, что и описание обычного атрибута класса, только оно не может содержать начального значения.
Основное назначение квалификатора ‒ снизить кратность противоположного полюса ассоциации, поэтому в основном он используется в ассоциациях с кратностями полюсов «один ко многим» или «многие ко многим» и стоит у полюса противоположному полюсу с кратностью «много».
При использовании квалификатора кратность противоположного полюса снижается, и это отображается на диаграмме.
Таким образом, если на полюсе ассоциации, противоположном полюсу квалификатора, задана кратность, то она указывает не допустимую мощность множества объектов, присоединенных к полюсу связи, а допустимую мощность того подмножества, которое определяется при задании значений атрибутов квалификатора.
Мы хотим подвести итог этому пространному разделу, обобщив сказанное с помощью диаграммы метамодели ассоциации, представленной на следующей диаграмме.
Рис. Метамодель ассоциации
3.3.9. Советы по проектированию
Нам хочется закончить обсуждение диаграмм классов ‒ важнейшего средства описания структуры в UML ‒ серией элементарных советов по практическому моделированию структуры. Как видно из предыдущих разделов, диаграммы классов содержат множество деталей. Для практически значимых систем диаграммы классов в конечном итоге получаются довольно сложными. Пытаться прорисовать сложную диаграмму классов сразу «на всю глубину» нерационально ‒ слишком велик риск «утонуть» в деталях. Мы полагаем, что удачная модель структуры сложной системы создается за несколько (может быть, даже за несколько десятков) итераций, в которых моделирование структуры перемежается моделированием поведения (см. главу 4). Вот наши советы.
Мы действительно сами следуем своим советам. Например, приведенные выше диаграммы это, как нетрудно видеть, промежуточные шаги при моделировании структуры предметной области нашей системы. Окончательный результат со всеми важными подробностями приведен ниже. Диаграмма достаточно хорошо читается ‒ в ней не слишком много деталей. Для уточнения можно использовать дополнительные диаграммы, иллюстрирующие определенные аспекты модели.
Рис. Основная диаграмма классов ИС ОК





