Меню

Применение каких отношений считается ошибкой при построении диаграммы классов

Предисловие

Парадигма объектно-ориентированного программирования (далее просто ООП) повсеместно используется при создании современного программного обеспечения. Модель объектов, заложенная в данную парадигму, способна достаточно точно описывать свойства и возможности сущностей реального мира. Разумеется, эти объекты не существуют обособленно друг от друга, они взаимодействуют друг с другом для достижения какой-то глобальной цели разрабатываемой системы.

Стандартная библиотека некоторого языка программирования – замечательный сборник полезных утилит. Однако разнообразие решаемых программистами задач так велико, что одной только стандартной библиотекой ограничиться не получится. Программисту часто приходится самому создавать необходимый ему набор функциональности. Это можно сделать, создав пакет функций или набор классов.

Создание собственных классов при разработке программы добавляет в проект новый уровень абстракции, который позволяет определить некоторый функционал системы и работать в дальнейшем только с ним.

Чем выше уровень абстракции, которым пользуется программист, тем выше уровень его продуктивности при разработке приложения.

Чем выше уровень абстракции, которым пользуется программист, тем выше уровень его продуктивности при разработке приложения.

«Хорошая абстракция превращает практически неподъемную задачу в две, решить которые вполне по силам. Первая из этих задач состоит в определении и реализации абстракции, а вторая — в использовании этих абстракций для решения текущей проблемы.»

Э.С. Таненбаум

Использование ООП может существенно упросить жизнь программисту. Это достигается за счёт сокрытия особенностей внутренней реализации классов. Программисту остаётся лишь пользоваться её удобствами. Кажется, что ООП – панацея от всех проблем. Однако на практике, если не иметь чёткого представления о том, какие классы нужно реализовать и как ими потом пользоваться, в результате может получиться очень запутанная система, которая начнёт порождать спагетти-коду (от англ. “spaghetti code”), который будет лишь мешаться, когда вы захотите добавить что-то новое в систему.

Чтобы избежать большинства проблем, возникающих при использовании ООП, нужно:

  1. Иметь некоторый опыт создания программ и использования классов.

  2. Строить структурные диаграммы классов.

Первое придёт со временем, а со вторым я могу вас познакомить прямо сейчас. Сегодня мы разберём диаграмму классов UML.


Содержание

  1. Назначение диаграммы классов

  2. Постановка задачи и её анализ

  3. Класс

    1. Статический класс

    2. Абстрактный класс

  4. Поля класса

    1. Уровень видимости

    2. Идентификатор

    3. Тип поля

    4. Кратность

  5. Методы класса

  6. Классы, отвечающие за графику

  7. Виды отношений

    1. Отношение ассоциации

    2. Отношение зависимости

    3. Отношение наследования

    4. Отношение агрегации

    5. Отношение композиции

Назначение диаграммы классов

Диаграмма классов (от англ. «class diagram») предназначена для представления внутренней структуры программы в виде классов и связей между ними. 

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

Взаимосвязь диаграммы классов с другими диаграммами

Диаграмма классов UML тесно связана с другими диаграммами, поскольку в них используются экземпляры классов (объекты), описанные на диаграмме классов. Например, на диаграмме кооперации (англ. «cooperation diagram») показывается структурные связи при взаимодействии объектов, а на диаграмме последовательности (англ. «sequence diagram») изображается последовательность обмена сообщений между объектами.

Постановка задачи и её анализ

Представьте, что мы хотим разработать программу для построения графиков функций.  Давайте кратко изобразим основной функционал такой программы на диаграмме вариантов использования UML:

Зачем нужен вариант использования «построить график функции»

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

Разумеется, мы хотим, чтобы наше приложение обладало графическим интерфейсом. На данном этапе разумно будет разбить задачу построения диаграммы на две части:

  1. Построить диаграмму для классов, ответственных за работу с пользовательскими математическими функциями.

  2. Построить диаграмму для классов, ответственных за корректное отображение графических элементов в программе.

Зачем плодить множество диаграмм, когда можно сделать одну большую?

Диаграмма классов строится прежде всего для внесения ясности в будущий процесс разработки системы. На первый взгляд, удобно всё классы построить на одной диаграмме, однако это может вызвать путаницу у читателей диаграммы, так как деталей будет слишком много.

Более правильным подходом станет выделение групп классов под различные задачи. Для каждой такой группы можно будет строить свою диаграмму. Чем меньше программисту нужно знать деталей для работы с какой-то частью программы, тем быстрее он сможет решить задачу.

Впоследствии можно построить упрощённую диаграмму для отображения взаимосвязей между классами из разных диаграмм.

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

Давайте определимся с тем, как мы собираемся работать с функциями. Во-первых, договоримся, что мы будем работать не напрямую с функциями, а только с их телами.

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

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

Поэтому мы создадим набор классов, которые будут «знать», как работать с такими данными. Упрощённая схема создания математических выражений представлена на рисунке ниже.

Для того чтобы мы могли работать с телом функции, записанным в виде строки, необходимо разбить эту строку на элементарные части. Их чаще всего называют лексемами (от англ. «lexeme») или токенами (от англ. «token»). В данной статье мы будем использовать термин токен. Получить список токенов математического выражения можно, используя алгоритм сортировочной станции.

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

На этом этапе математическое выражение мы будем считать обработанным и готовым к работе. Чтобы вычислить значение этого выражения, мы будем подставлять вместе переменной какое-либо действительное значение.

Для удобства работы с нашим приложением нужно добавить возможность определения и использования именованных констант. Например, использование распространённых математических констант, таких как π = 3,141592.. или e = 2,71828.. будет очень удобным для пользователей.

В заключение, хочется отметить, что каждое серьёзной программе необходим механизм обработки ошибок. Наша программа не будет исключением. Пользовательские выражения будут проверяться на корректность математической записи.

Соглашение об именовании объектов и классов

Несмотря на то, что данную статью я пишу на русском языке, на диаграммах мы всё будем описывать на английском языке. Для этого есть две причины:

  1. Когда вы начнёте реализовывать классы, описанные на диаграмме, в коде, вы будете давать им название на английском языке. Разные программисты по-разному могут перевести одно и то же слово. Например, класс «МатематическоеВыражение» некоторые могут перевести как «MathExpression», другие, исходя из специфика задачи, как «Function» или просто как «Expression». Таким образом, когда вы начнёте сопоставлять написанные классы с элементами на диаграмме, вы можете запутаться.

  2. Технический английский язык обычно более ёмок, чем русский. Это позволит сократить объём текста на диаграмме.

Давайте подведём итог и выясним, какие классы нам будут нужны для решения поставленной задачи:

  • Класс для хранения математического выражения. Назовём его MathExpression.

  • Класс для разбиения строкового представления выражения на список токенов — MathParser.

  • Класс для построения постфиксной формы выражения из списка токенов — MathFormConverter.

  • Класс для работы с именованными константами — MathConstantManager.

  • Класс для проверки корректности пользовательского математического выражения — MathChecker.

  • Класс для подсчёта таблицы значений математического выражения — MathCalculator.

Почему все имена классов имеют префикс Math?*

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

Более правильным решением было бы вынести все эти классы в пространство имён Math и убрать префикс из имён. Однако продолжим работать с нашим Legacy-кодом.

Класс

Класс — элемент диаграммы, обозначающий множество объектов, обладающих одинаковой внутренней структурой, поведением и отношениями с объектами других классов. Изображается класс на диаграмме в виде прямоугольника, разделённого на три секции:

  1. Имя класса

  2. Список полей класса

  3. Список методов класса

Выбор терминологии

В различной технической литературе вы можете встретить альтернативные названия для этих терминов:

  1. Поля (от англ. “field”) <=> свойства (от англ. “properties”), атрибуты (от англ. “attributes”)

  2. Методы (от англ. “method”) <=> функции (от англ. “functions”), операции (от англ. “operations”)

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

Обязательным элементом класса является только его название.

Обязательным элементом класса является только его название.

Оранжевым цветом мы будем выделять обязательные части элементов.

Пример класса "Покупатель". У покупателя есть баланс (balance) денег и список желаемого (wishList). Пользователь может пополнять баланс на некоторую сумму денег (topUpBalance()), может совершать покупки (makePurchase()) и может добавлять товары в список желаемого (appendToWishList()). Также мы можем проверить, подтверждена ли электронная почта пользователя.

Пример класса «Покупатель». У покупателя есть баланс (balance) денег и список желаемого (wishList). Пользователь может пополнять баланс на некоторую сумму денег (topUpBalance()), может совершать покупки (makePurchase()) и может добавлять товары в список желаемого (appendToWishList()). Также мы можем проверить, подтверждена ли электронная почта пользователя.

Обычно в качестве имени класса выбирается существительное в единственном числе. Разумеется, это имя должно быть уникальным в пределах диаграммы. Если имя класса состоит из нескольких слов, мы ,по практическим соображениям, будем записывать их слитно в верблюжьем стиле (от англ. «CamelCase»).

Рекомендация по именованию классов

Определение класса схоже с определением актёра в том смысле, что оба эти элемента обозначают группу объектов и пользователей соответственно. В связи с этим оба эти элемента должны именоваться существительным в единственном числе.

Настало время изобразить наши классы на диаграмме. Пока что давайте изобразим только имена этих классов.

Первая версия диаграмма классов, которые отвечают за работу
с математическими выражениями.

Первая версия диаграмма классов, которые отвечают за работу
с математическими выражениями.

Пока что эта диаграмма не даёт никакого понимания того, как будет устроена наша система, однако к концу статьи диаграмма значительно преобразится.

Статический класс

Класс, в котором есть только статические поля и методы и на основе которого не создаются объекты,  называется статическим классом. Чтобы показать на диаграмме, что наш класс статический, нужно добавить к имени модификатор «utility».

Формально, такие модификаторы называется стереотипами. Стереотип – именованный набор свойств. В данном случае, стереотип «utility» означает, что объекты указанного класса не создаются.

По сути, название модификатора «utility» связано с тем, статический класс предоставляет набор утилит, которые могут быть использованы любыми классами, которые в них нуждаются.

В нашей системе классы MathParser, MathFormConverter, MathConstantManager являются статическими, потому что они представляют собой «сборник» полезных функций, которые мы объединили в класс. Давайте изобразим это на нашей диаграмме.

2 версия диаграммы

2 версия диаграммы

Абстрактный класс

Класс, который является базовым для других классов и объекты которого мы не собираемся создавать, называют абстрактным. Абстрактный класс на диаграмме изображается так же, как и обычной класс, однако имя такого класса должно быть записано курсивом.

В UML принято соглашение, согласно которому все элементы, относящиеся к абстрактному классу, должны быть помечены курсивом (жирный шрифт при этом сохраняется).

Поля класса

Вернёмся к нашему примеру с классом Customer. Обратите внимание на центральную секцию.

Давайте рассмотрим первую строчку. Что вообще означает запись «- balance: Integer»? Сейчас будем разбираться.

В общем случае, каждое поле класса должно описываться следующим образом:

Общий вид поля класса

Общий вид поля класса

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

Уровень видимости

Уровень видимости (от англ. «visibility») — свойство поля, которое показывает, из какой части программы можно обратиться к данному полю.

В нашем случае, поле balance - закрытое

В нашем случае, поле balance — закрытое

Обычно может принимать следующие значения:

  • «+» — открытое поле. Аналог public в языках программирования. Означает, что к полю можно обратиться из любой части программы.

  • «-» — закрытое поле. Аналог private в языках программирования. Означает, что получить доступ к полю можно только внутри класса.

  • «#» — защищённое поле. Аналог protected в языках программирования. Означает, что получить доступ к полю можно внутри класса и внутри производных классов.

Может показаться, что как-то неудобно для каждого поля указывать его уровень видимости. Почему бы не группировать поля по уровню видимости? Например, именно такой подход используется в языке программирования C++. Давайте попробуем напрямую использовать ключевые слова public, private и protected.

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

А что будет, если не указывать уровень видимости вовсе?*

В некоторых языках программирования, к примеру, в C++ или C# отсутствие уровня видимости поля или метода по умолчанию означает, что такой элемент считается закрытым. Тем не менее, обычно уровень видимости указывается для каждого поля, чтобы код становился более читабельным.

В UML, если уровень видимости не указан, то никакого значения по умолчанию не подразумевается. Таким образом, в данной статье мы всегда будем указывать уровень видимости для наших элементов.

Идентификатор

Идентификатор (от англ. «identificator») — название поля. Является обязательным элементом для описания переменной на диаграмме классов, поскольку однозначно её определяет (все идентификаторы на диаграммах уникальны).

Тип поля

Тип поля (англ. «type of field») показывает, какой тип имеет данное поле в нашей программе. На ранней стадии проектирования можно и не уточнять, какой тип имеет то или иное поле.

Типы полей обычно привязаны к какому-то конкретному языку программирования, например. Кроме того, в качестве типа поля может быть использован пользовательский тип данных.

Кратность

Кратность (от англ. «multiplicity») – интервал, определяющий диапазон количества элементов в массиве. Если для поля указана кратность, то его следует считать массивом. Количество элементов в таком массиве и будет определяться указанным интервалом.

Список желаемого - это массив, который может либо быть пустым, либо может хранить неограниченное число товаров.

Список желаемого — это массив, который может либо быть пустым, либо может хранить неограниченное число товаров.
Пояснение к использованию кратности

Multiplicity is a definition of an inclusive interval of non-negative integers to specify the allowable number of instances of described element.

Кратность определяет отрезок с неотрицательными целочисленными границами, который показывает допустимое количество объектов определённого типа.

“Кратность, если она присутствует, определяет данный атрибут как массив (определенной или неопределенной длины).”

Учебно-методическое пособие по дисциплине «Анализ и проектирование на UML». ИТМО.

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

Для кратности указывают одно или два значения:

  • [m..n] — интервал от m до n включительно (m <= n). Такая запись будет означать, что в коллекции может храниться от m до n значений включительно.

  • [n] – интервал, который можно рассматривать, как сокращённую запись [0..n].

Может случиться так, что мы захотим показать, что в массиве может храниться неограниченное количество элементов. В таком случае верхняя граница n заменяется символом *.

Примеры интервалов:

  • [1] — ровно один объект. То же самое, что и интервал [1..1]

  • [0..1] — ноль или один объект.

  • [0..*] — ноль или неограниченное количество объектов. Часто такой интервал обозначают просто как [*].

  • [1..*] — один или неограниченное количество объектов.

Наиболее часто используют кратность [0..*] или [1..*]. Можно заметить, что динамические структуры данных вообще очень удобны в использовании. В нашей статье мы откажемся от использования кратности, а будем использовать такие коллекции: связный список (List) и ассоциативный массив (Map).

С использованием кратности мы ещё столкнёмся, когда будем знакомиться с различными типами отношений. Теперь, когда мы немного познакомились с правилами описания полей классов, давайте опишем некоторые поля для классов нашего проекта.

3 версия диаграммы

3 версия диаграммы

Чтобы отличать статические элементы класса от обычных, статические поля и методы будут подчёркиваться.

Назначение каждого поля построенных классов

Диаграмма классов UML хорошо показывает отношения между классами, однако она не может многого сказать о назначении полей и методов в классах. Если вы хотите лучше понять, для чего нужно каждое поле на диаграмме, прочтите об этом ниже.

Класс MathExpression:

  • initial – строковое математическое выражение, записанное в инфиксной форме.

  • postfix – строковое математическое выражение, записанное в постфиксной форме.

  • parameters – параметры в математическом выражении. Всего параметров четыре: a, b, c, d. У каждого параметра могут быть произвольные действительные значения. Значения параметров хранятся в ассоциативном массиве.

Класс MathParser:

  • delimiters — список разделителей. С помощью этих разделителей удаётся простым образом разбить входное выражение на список токенов.

Класс MathFormConverter:

  • precedence — ассоциированный массив, хранящий приоритет каждой операции/функции. Это необходимо для перевода выражения из инфиксной формы в постфиксную.

Класс MathConstantManager:

  • userDefinedConstants — ассоциированный массив, хранящий значения определённых пользователем констант.

  • predefinedConstants — ассоциированный массив, хранящий значения предопределённых констант.

Класс MathChecker:

  • userExpression — строковое представление пользовательского математического выражения, которое будет проверяться.

  • errorPlace — участок строки, в котором содержится ошибка (например, неизвестный системе токен).

  • errorType — тип ошибки.

  • operations — список корректных операций.

  • functions — список корректных функций.

  • operandQuantity — ассоциированный массив, хранящий количество аргументов для каждой операции/функции.

Класс MathCalculator:

  • expression — объект математического выражения, значения которого будут подсчитываться.

Аналогичное примечание будет сделано и для методов классов.

Методы класса

Снова разберём наш пример с классом Customer. На этот раз обратим внимание на третью секцию — секцию методов.

Описание методов очень похоже на описание полей класса. На рисунке ниже представлен общий вид описания метода класса.

Аргументы методов в общем случае описываются следующим образом:

В случае, если у метода есть несколько аргументов, они указываются в скобках через запятую
в указанном формате.
В случае, если у метода есть несколько аргументов, они указываются в скобках через запятую
в указанном формате.

Теперь давайте добавим на нашу основную диаграмму  методы классов.

Назначение каждого метода построенных классов

Класс MathExpression:

  • setParameter(parameter, value) — устанавливает параметру parameter значение value.

  • setExpression(expression) — устанавливает новое тело функции математического выражения.

  • getStringRepresentation() — возвращает тело функции.

Класс MathParser:

  • CreateTokenList(expression) — разбивает математическое выражение на список токенов и возвращает его в виде списка строк.

Класс MathFormConverter:

  • InfixToPostfix(infixExpression) — переводит математическое выражение в постфиксную форму.

Класс MathConstantManager:

  • addConstant(constant, value) — добавляет в систему новую пользовательскую константу.

  • alterConstant(constant, newValue) — изменяет значение пользовательской константы constant на newValue.

  • deleteConstant(constant) — удаляет пользовательскую константу constant.

  • getConstantValue(constant) — возвращает значение пользовательской константы constant.

  • isConstant(token) — проверяет, является ли token константой.

Класс MathChecker:

  • areAllTokensCorrect() — проверяет все токены математического выражения на корректность.

  • areBracketsCorrespond() — проверяет все ли скобки расставлены правильно.

  • hasEmptyBrackets() — проверяет, есть ли в выражении пустые скобки

  • hasMissedOperations() — проверяет, есть ли в выражении пропущенные операции. Например, для отслеживания случаев «18 354» — между числами пропущена операция.

  • IsOperation(token) — проверяет, является ли токен корректной операцией.

Класс MathCalculator:

  • calculate(value) — подсчитывает значение математического выражения, когда значение переменной равно value

Классы, отвечающие за графику

Помните, мы в начале статьи договорились, что у нас будет две диаграммы: одна для классов, отвечающих за работу с функциями, другая – для классов, отвечающих за графику? Настало время построить вторую диаграмму. Результат представлен на картинке ниже.

Слишком много непонятных классов

Давайте вместе разбираться в этой куче классов:

  • QWidget – стандартный класс фреймфорка Qt, который является базовым почти для всех создаваемых виджетов (графических элементов). В нашем проекте все элементы созданы на основе этого класса.

  • QDialog – стандартный класс для создания диалоговых окон.

  • AboutProgramDialog – окно информации о программе. Такое окно есть почти в каждой программе. В нём находится краткое описание проекта, его авторы и, возможно, информация о лицензировании.

  • HelpDialog – окно справочной информации. В больших системах справочник просто необходим.

  • MainWindow – главное окно программы «Построитель графиков функций». В нём содержатся основные элементы программы: плоскость для построения графиков (PaintingArea), список блоков ввода функций (FunctionBoxList), список блоков ввода констант (ConstantBoxList).

  • PaintingArea – плоскость для построения графиков. Пользователь может захотеть нарисовать графики некоторых определённых им функций. За отображения этих графиков и отвечает данный класс.

Координатная плоскость для построения графиков. Является объектом класса PaintingArea. В качестве примера, на координатной плоскости построен график функции y=sin(x)

Координатная плоскость для построения графиков. Является объектом класса PaintingArea. В качестве примера, на координатной плоскости построен график функции y=sin(x)
  • Graph – график функции на координатной плоскости. Пример объекта класса Graph представлен на рисунке выше.

  • FunctionBox – блок ввода функции. Представлен на рисунке ниже.

  • FunctionBoxList – список блоков ввода функций.

  • ConstantBox – блок ввода константы. Представлен на рисунке ниже.

  • ConstantBoxList – список блоков ввода констант.

Виды отношений

Давайте начнём рассматривать различные отношения между классами на диаграмме. Нами будут рассмотрены следующие соединительные линии:

  • Отношение ассоциации

  • Отношение зависимости

  • Отношение обобщения, также известное как отношение наследования.

  • Отношение агрегации

  • Отношение композиции

Обозначение каждого вида отношения представлено на рисунке ниже. Далее мы начнём рассматривать каждое отношение в подробностях.

Рассматриваемые нами виды отношений

Рассматриваемые нами виды отношений

Отношение ассоциации

Отношение ассоциации используют, чтобы показать, что между классами (например, между двумя классами) существует некоторая связь. Обычно с помощью него на диаграмме классов показывают, что один класс пользуется функционалом другого класса.

Методы класса LogSystem используют метод Console::WriteLine() и, возможно, некоторые 
другие  для вывода результатов.

Методы класса LogSystem используют метод Console::WriteLine() и, возможно, некоторые
другие для вывода результатов.

В общем случае, использование отношения ассоциации выглядит следующим образом:

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

Обратите внимание на кратность ассоциации, которая расположена под стрелкой. С кратностью мы уже встречались ранее. Здесь у нее несколько иное значение. Кратность ассоциации обозначает количество объектов, которые участвуют во взаимодействии. Как показано на рисунке выше, во взаимодействии могут участвовать от m до n пользователей и от q до r владельцев.

Клиенты могут подключаться к серверу. С помощью кратности ассоциации указывается допустимое количество объектов в таком взаимодействии.

Клиенты могут подключаться к серверу. С помощью кратности ассоциации указывается допустимое количество объектов в таком взаимодействии.

Если кратность ассоциации не указана, будет подразумеваться кратность [0..*]. В случае со статическими классами кратность не указывается (можно считать, что там указана кратность [1].

Избегайте использования отношения ассоциации в обе стороны, как это показано на рисунке ниже. Такое взаимодействие классов можно считать ярким примером спагетти-кода. В случае ошибки очень сложно будет обнаружить, что послужило ей причиной.

Обозначение отношения ассоциации

Строго говоря, отношение ассоциации изображают сплошной линией без стрелок. Например, на различных сайтах вы, скорее всего, могли видеть следующие примеры.

В таких стандартных примерах очевидно, что студент учится у преподавателей и что работник работает на компанию, и никак иначе. Однако людям, которые только начинают знакомиться с вашей программой, необходимо понимать, в какую сторону направлена стрелка. Таким образом, возникает потребность в использовании именно направленного отношения ассоциации. В дальнейшем мы будем обозначать ассоциацию сплошной линией со стрелкой.

Давайте добавим отношения ассоциации на наши диаграммы.

С помощью отношения ассоциации мы в общих чертах показываем, как взаимодействуют классы.

С помощью отношения ассоциации мы в общих чертах показываем, как взаимодействуют классы.
Когда пользователь создаёт блок ввода константы и заполняет все поля, в системе создаётся новая константа. За это отвечает статический класс MathConstantManager.
Когда пользователь создаёт блок ввода константы и заполняет все поля, в системе создаётся новая константа. За это отвечает статический класс MathConstantManager.

Отношение зависимости

Отношение зависимости используют, чтобы показать, что изменение одного класса требует изменение другого класса.

В нашем случае вид графика функции зависит от самой функции или, по нашей договорённости об именовании, от объекта математического выражения. Давайте покажем это на диаграмме.

Изменение объекта математического выражения влияет на вид графика.

Изменение объекта математического выражения влияет на вид графика.

Стрелка отношения зависимости направлена от зависимого класса к независимому.

Объекты класса Graph зависят от изменений в объектах класса MathExpression, поскольку, на самом деле, в объекте Graph хранится указатель на объект класса MathExpression. Поэтому мы можем считать, что график «знает» обо всех изменениях внутри математического выражения (изменение тела функции, изменение границ значений переменной, изменение значения параметров и т.д.).

Отношение между этими двумя классами как бы соединяет две диаграммы воедино. Все классы нашей первой диаграммы «работают» на объекты класса MathExpression. Людям, работающим с графической частью приложения, нужно знать лишь об этом классе, что существенно снижает сложность. В результате наша вторая диаграмма приобретает следующий вид.

Отношение наследования

Прежде чем мы начнём изучать данное отношение, проясним один момент. Как вы можете видеть, данное отношение имеет два названия: отношение обобщения и отношение наследования. В терминах ООП принцип наследования является очень важной вещью. Чтобы не вносить путаницу в дальнейшее повествование, давайте договоримся использовать только второе название – отношение наследования – применительно к диаграмме классов.

Итак, отношение наследования используется, чтобы показать, что один класс является родителем (базовым классом или суперклассом) для другого класса (потомка, производного класса).

Если вы работали с какой-нибудь библиотекой для создания графического интерфейса (OpenGL в чистом виде не в счёт!), вы могли заметить, что все классы графических элементов обычно выстраиваются в цепочку наследования.  Например, взгляните на цепочку наследования классов фреймворка Qt5, представленную на рисунке ниже.

Отношение наследования здесь изображается обычной стрелкой.

Отношение наследования здесь изображается обычной стрелкой.
Полная версия диаграммы

Полную версию диаграммы вы можете посмотреть по ссылке

Разумеется, такие цепочки наследования просто необходимы, поскольку различные виджеты (графические элементы) имеют схожие свойства и поведение. В нашем приложении классы виджетов тоже были созданы на основе нескольких базовых классов: QWidget и QDialog. Давайте покажем это на диаграмме.

Мы свернули классы ради экономия места. В дальнейшем мы также будем это делать, поскольку нас будет интересовать именно отношения между классами, а не их поля и методы. Заметьте, что в большинстве наших классов мы не можем обойтись без класса QWidget и QDialog, однако его использование приводит к беспорядку.

Давайте договоримся, что больше мы не будем изображать эти два базовых класса, чтобы избежать проблем с читабельностью диаграммы. Тем не менее, мы будем иметь в виду, от каких классов наследуются оставшиеся.

Отношение агрегации

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

В переводе с английского, слово aggregation означает соединение частей. Это значение очень точно отражает суть данного отношения – показать, из каких частей состоит класс.

В целом, смысл этого отношения достаточно условен. Может показаться, что определение отношения агрегации «один класс включает в себя другой класс» означает примерно следующее:

class A
{
	class B
	{
	};
…
}; 

На самом деле, это отношение означает, что объект одного класса включает в себя в качестве составной части объект другого класса.

Объект класса PersonalComputer (упрощённо) состоит из объекта класса Monitor, объекта класса ComputerMouse и объекта класса Keyboard.

Объект класса PersonalComputer (упрощённо) состоит из объекта класса Monitor, объекта класса ComputerMouse и объекта класса Keyboard.

С отношением агрегации также можно использовать кратность, чтобы показать, сколько объектов одного класса входят в состав объекта другого класса.

На нашей диаграмме есть много мест, где нам может пригодиться отношение агрегации:

  • Объект класса MainWindow содержит в себе по одному объекту классов PaintingArea, ConstantBoxList, FunctionBoxList.

  • Неограниченное количество объектов класса Graph могут содержаться в объекте класса PaintingArea.

  • Класс-контейнер ConstantBoxList может содержать в себе неограниченное количество объектов класса ConstantBox.

  • Класс-контейнер FunctionBoxList может содержать в себе неограниченное количество объектов класса FunctionBox.

Отношение композиции

Отношение композиции является частным случаем отношения агрегации. Однако у него есть одно отличие – классы-части, которые он соединяет с классом-целым, не могут существовать обособленно.

Одним из переводов слова composition является слово смесь. Если допустить, что из смеси не получится получить исходные компоненты, то это хорошо помогает понять, что части, соединённые отношением композиции не могут существовать сами по себе.

Давайте в качестве примера рассмотрим окно интерпретатора Python.

Понятное дело, что ни полоса прокрутки (ScrollBar), ни заголовок окна (Title), ни поле ввода команд (TextInput) не могут существовать отдельно от окна программы (Window). Это можно изобразить на диаграмме классов следующим образом.

В нашей диаграмме объекты классов FunctionBox и ConstantBox не могут существовать отдельно от их контейнеров. Кроме того, объекты класса Graph тоже не могут существовать обособленно от координатной плоскости.

Вот и всё! Мы рассмотрели достаточно элементов диаграммы классов, чтобы начать делать собственные диаграммы классов.

P.S. Спасибо всем, кто дошёл до этого момента. Статья получилась очень массивной, поскольку хотелось разобрать все основные элементы на практическом примере.

Аннотация: Отношение ассоциации, варианты его графического изображения. Отношение обобщения классов. Наследование атрибутов и операций классов. Отношения агрегации и композиции, их семантические особенности. Рекомендации по построению диаграмм классов.

Кроме внутреннего устройства классов важную роль при разработке проектируемой системы имеют различные отношения между классами, которые также могут быть изображены на диаграмме классов. Совокупность допустимых типов таких отношений строго фиксирована в языке UML и определяется самой семантикой этих отношений. Базовые отношения, изображаемые на диаграммах классов:

  • Отношение ассоциации (association relationship)
  • Отношение обобщения (generalization relationship)
  • Отношение агрегации (aggregation relationship)
  • Отношение композиции (composition relationship)

Каждое из этих отношений имеет собственное графическое представление, которое отражает семантический характер взаимосвязи между объектами соответствующих классов.

Отношение ассоциации

Ассоциация (association) — семантическое отношение между двумя и более классами, которое специфицирует характер связи между соответствующими экземплярами этих классов.

Отношение ассоциации соответствует наличию произвольного отношения или взаимосвязи между классами. Данное отношение, как уже отмечалось в лекции 3, обозначается сплошной линией со стрелкой или без нее и с дополнительными символами, которые характеризуют специальные свойства ассоциации. Ассоциации рассматривались при изучении элементов диаграммы вариантов использования, применительно к диаграммам классов, тем не менее, семантика этого типа отношений значительно шире. В качестве дополнительных специальных символов могут использоваться имя ассоциации, символ навигации, а также имена и кратность классов-ролей ассоциации.

Имя ассоциации — необязательный элемент ее обозначения. Однако если оно задано, то записывается с заглавной буквы рядом с линией ассоциации. Отдельные классы ассоциации могут играть определенную роль в соответствующем отношении, на что явно указывает имя концевых точек ассоциации на диаграмме.

Наиболее простой случай данного отношения — бинарная ассоциация (binary association), которая служит для представления произвольного отношения между двумя классами. Она связывает в точности два различных класса и может быть ненаправленным (симметричным) или направленным отношением. Частный случай бинарной ассоциациирефлексивная ассоциация, которая связывает класс с самим собой. Ненаправленная бинарная ассоциация изображается линией без стрелки. Для нее на диаграмме может быть указан порядок чтения классов с использованием значка в форме треугольника рядом с именем данной ассоциации.

В качестве простого примера ненаправленной бинарной ассоциации можно рассмотреть отношение между двумя классами — классом Компания и классом Сотрудник (рис. 6.1). Они связаны между собой бинарной ассоциацией Работает, имя которой указано на рисунке рядом с линией ассоциации. Для данного отношения определен следующий порядок чтения следования классов — сотрудник работает в компании.

Графическое изображение ненаправленной бинарной ассоциации между классами

Рис.
6.1.
Графическое изображение ненаправленной бинарной ассоциации между классами

Направленная бинарная ассоциация изображается сплошной линией с простой стрелкой на одной из ее концевых точек. Направление этой стрелки указывает на то, какой класс является первым, а какой — вторым.

В качестве простого примера направленной бинарной ассоциации можно рассмотреть отношение между двумя классами — классом Клиент и классом Счет (рис. 6.2). Они связаны между собой бинарной ассоциацией с именем Имеет, для которой определен порядок следования классов. Это означает, что конкретный объект класса Клиент всегда должен указываться первым при рассмотрении взаимосвязи с объектом класса Счет. Другими словами, эти объекты классов образуют кортеж элементов, например, <клиент, счет_1, счет_2,…, счет_n>.

Графическое изображение направленной бинарной ассоциации между классами

Рис.
6.2.
Графическое изображение направленной бинарной ассоциации между классами

Частный случай отношения ассоциации — так называемая исключающая ассоциация (Xorassociation). Семантика данной ассоциации указывает на то, что из нескольких потенциально возможных вариантов данной ассоциации в каждый момент времени может использоваться только один. На диаграмме классов исключающая ассоциация изображается пунктирной линией, соединяющей две и более ассоциации (рис. 6.3), рядом с которой записывается ограничение в форме строки текста в фигурных скобках: {xor}.

Графическое изображение исключающей ассоциации между тремя классами

Рис.
6.3.
Графическое изображение исключающей ассоциации между тремя классами

Тернарная ассоциация связывает отношением три класса. Ассоциация более высокой арности называется n-арной ассоциацией.

n-арная ассоциация (n-ary association) — ассоциация между тремя и большим числом классов.

Каждый экземпляр такой ассоциации представляет собой упорядоченный набор (кортеж), содержащий n экземпляров из соответствующих классов. Такая ассоциация связывает отношением более чем три класса, при этом класс может участвовать в ассоциации более чем один раз. Каждый экземпляр n-арной ассоциации представляет собой n-арный кортеж, состоящий из объектов соответствующих классов. В этом контексте бинарная ассоциация является частным случаем n-арной ассоциации, когда значение n=2, но имеет собственное обозначение. Бинарная ассоциация — это специальный случай n-арной ассоциации.

Графически n-арная ассоциация обозначается ромбом, от которого ведут линии к символам классов данной ассоциации. Сам же ромб соединяется с символами классов сплошными линиями. Обычно линии проводятся от вершин ромба или от середины его сторон. Имя n-арной ассоциации записывается рядом с ромбом соответствующей ассоциации. Однако порядок классов в n-арной ассоциации, в отличие от порядка множеств в отношении, на диаграмме не фиксируется.

В качестве примера тернарной ассоциации можно рассмотреть отношение между тремя классами: Сотрудник, Компания и Проект. Данная ассоциация указывает на наличие отношения между этими тремя классами, которое может представлять информацию о проектах, реализуемых в компании, и о сотрудниках, участвующих в выполнении отдельных проектов (рис. 6.4).

Графическое изображение тернарной ассоциации между тремя классами

Рис.
6.4.
Графическое изображение тернарной ассоциации между тремя классами

Класс может быть присоединен к линии ассоциации пунктирной линией. Это означает, что данный класс обеспечивает поддержку свойств соответствующей n-арной ассоциации, а сама n-арная ассоциация имеет атрибуты, операции и/или ассоциации. Другими словами, такая ассоциация является классом с соответствующим обозначением в виде прямоугольника и самостоятельным элементом языка UML — ассоциативным классом (Association Class).

Класс ассоциация (association class) — модельный элемент, который одновременно является ассоциацией и классом. Ассоциация класс может рассматриваться как ассоциация, которая обладает свойствами класса, или как класс, имеющий также свойства ассоциации.

Как уже упоминалось, отдельный класс в ассоциации может играть определенную роль в данной ассоциации. Эта роль может быть явно специфицирована на диаграмме классов. С этой целью в языке UML вводится в рассмотрение специальный элемент — концевая точка ассоциации или конец ассоциации (Association End), который графически соответствует точке соединения линии ассоциации с отдельным классом.

Конец ассоциации (association end) — концевая точка ассоциации, которая связывает ассоциацию с классификатором.

Конец ассоциации — часть ассоциации, но не класса. Каждая ассоциация может иметь два или больше концов ассоциации. Наиболее важные свойства ассоциации указываются на диаграмме рядом с этими элементами ассоциации и должны перемещаться вместе с ними. Одним из таких дополнительных обозначений является имя роли отдельного класса, входящего в ассоциацию.

Роль (role) — имеющее имя специфическое поведение некоторой сущности, рассматриваемой в определенном контексте. Роль может быть статической или динамической.

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

Следующий элемент обозначений — кратность ассоциации. Кратность относится к концам ассоциации и обозначается в виде интервала целых чисел, аналогично кратности атрибутов и операций классов, но без прямых скобок. Этот интервал записывается рядом с концом соответствующей ассоциации и означает потенциальное число отдельных экземпляров класса, которые могут иметь место, когда остальные экземпляры или объекты классов фиксированы.

Так, для примера (рис. 6.4) кратность » 1 » для класса Компания означает, что каждый сотрудник может работать только в одной компании. Кратность » 1..* » для класса Сотрудник означает, что в каждой компании могут работать несколько сотрудников, общее число которых заранее неизвестно и ничем не ограничено. Вместо кратности » 1..* » нельзя записать только символ » * «, поскольку последний означает кратность » 0..* «. Для данного примера это означало бы, что отдельные компании могут совсем не иметь сотрудников в своем штате. Такая кратность приемлема в ситуациях, когда в компании вообще не выполняется никаких проектов.

Имя ассоциации рассматривается в качестве отдельного атрибута у соответствующих классов ассоциаций и может быть указано на диаграмме явным способом в определенной секции прямоугольника класса.

Ассоциация является наиболее общей формой отношения в языке UML. Все другие типы рассматриваемых отношений можно считать частным случаем данного отношения. Однако важность выделения специфических семантических свойств и дополнительных характеристик для других типов отношений обусловливают необходимость их самостоятельного изучения при построении диаграмм классов. Поскольку эти отношения имеют специальные обозначения и относятся к базовым понятиям языка UML, следует перейти к их последовательному рассмотрению.

Типы отношений на диаграмме классов

При
построении
диаграмме
классов
могут
быть
использованы
следующие
отношения:

  • Отношение
    зависимости
    (dependency
    relationship
    ).

  • Отношение
    ассоциации
    (association
    relationship
    ).

  • Отношение
    обобщения
    (generalization
    relationship
    ).

  • Отношение
    реализации
    (realization
    relationship
    ).

Отношение
зависимости
является
наиболее
общей
формой
отношения
между
классами.
На
диаграмме
данное
отношение
изображается
пунктирной
линией
со
стрелкой,
направленной
от
зависимого
класса
к
источнику
зависимости.
Отношение
зависимости
используется
в
ситуации,
когда
изменения
одного
класса
могут
потребовать
изменения
другого
зависимого
от
него
класса.
На
рис.
4.4 представлена
следующая
ситуация
— свойства
объектов
класса
Заказ
автомобиля
зависят
от
свойств
объектов
класса
Автомобиль.

Рис. 4.4. Пример отношения
зависимостимежду двумяклассами.

Для
отношения
а
ссоциации
используется
понятие
арности
— количество
классов,
участвующих
в
данном
отношении.
Бинарная
ассоциация

отношении
участвуют
два
класса)
является
частным
случаем
N-арной
ассоциации.

Специальной
формой
отношения
ассоциации
является
отношение
агрегации,
которое
также
может
быть
любой
арности.
Отношение
агрегации

используется в том случае, когда один
класс
(класс-целое) может быть представлен
декомпозицией других классов
(класс-часть) см. рис.4.5.

Рис. 4.5. Пример отношения агрегациимежду классами

Частным случаем отношения
агрегации является отношение
композиции
см. рис.4.6. Отношение
композиции
показывает,
что не может существовать отдельно от
класса-целое
(либо для того чтобы подчеркнуть, что
класс-часть
имеет смысл рассматривать только как
составную часть другого класса).
Следует заметить, что в зависимости от
решаемых задач и от свойств предметной
области между одними и теми же классами
может быть установлено в одном случае
отношение композиции,
в другом – отношение
агрегации
.

Рис. 4.6. Пример
отношения
композиции

(частный случай отношения
агрегации
)
между классами.

Отношение
обобщения

является отношением между более общим
классом
(классом-родителем)
и более частным классом
(классом-потомком).
При этом предполагается, что класс-потомок
обладает всеми свойствами и поведением
класса-потомка,
а также может иметь свои собственные
свойства и поведение, которые отсутствуют
у класса-родителя
см. рис. 4.7 (классы
Голкипер, Защитник, Играющий тренер
наследуют свойства и поведение
родительского
класса

Футболист, класс
Играющий тренер наследует свойства и
поведения класса
Тренер). Наследуются потомками
не все свойства класса-родителя
(атрибуты и методы), а только с областью
видимости общей и закрытой (public,
protect).

Рис.
4.7. Пример отношения
обобщения

между классами.

Выявление классов (одна из основных задач проектирования системы- определить классы и отношения между ними)

Для
выявления
классов
следует
рассмотреть
документацию
к
дисграммам
прецедентов.
Имена
существительные,
которые
участвуют
в
этом
описании,
позволят
определить
классы.
Имена
существительные
могут
стать:
актёрами,
классами

как
следствием
объектом
этого
класса
для
построения
диаграммы
последовательности)
или
атрибутами
классов.

Вторым
способом
является
анализ
диаграмм
последовательностей.
Но
этих
диаграмма
следует
найти
схожие
объекты,
т.е.
те
объекты,
которые
обладают
одинаковыми
данными
и
поведением.
Пример.
На
диаграммах
последовательности
участвуют
два
объекта
Счет
Мистера
Иванова
и
Cчет
Миссис
Петровой.
Оба
счета
обладают
такими
свойствами
как
баланс
и
пароль,
оба
счета
можно
пополнять,
проверять
пароль
и
т.д.
Тогда
следует
создать
класс
Счёт.

Но
не
все
классы
можно
получить
из
анализа
диаграммы
последовательности
или
документации
с
описанием
сценариев
поведения
прецедентов.
После
выявления
классов
указанными
способами
следует
еще
проанализировать
систему
по
трем
направлениям:

  • классы,
    необходимые
    для
    описания
    основных
    сущностей
    (стереотип
    <<Entity>>);

Классысущности
присутствуют
на
каждой
диаграмме
классов.
Они
описывают
основные
понятия
системы.

  • классы,
    необходимые
    для
    управления
    системой
    и
    её
    элементами
    (стереотип
    <<Control>>);

Управляющие
классы
отвечают
за
координацию
действий
других
классов.
Представителями
могут
быть
следующие
классы:
Менеджер,
Менеджер
безопасности,
Менеджер
взаимодействия
и
т.д.

  • классы,
    необходимые
    для
    связи
    с
    внешним
    миром
    (стереотип
    <<Boundary>>).

Пограничные
классы
расположены
на
границе
системы
со
всем
остальным
миром.
Они
включают
в
себя
формы,
отчёты,
интерфейсы
с
аппаратурой
(принтеры,
сканеры),
интерфейсы
с
другими
системами.
Для
выявления
пограничных
классов
следует
проанализировать
диаграмму
прецедентов.
Для
каждого
актера
связанного
с
прецедентом,
должен
быть
пограничный
класс,
который
позволяет
данному
актёру
(внешней
сущности)
взаимодействовать
с
системой,
чтобы
получить
соответствующий
сервис.
Для
нескольких
актёров
можно
построить
один
«пограничный»
класс.

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]

  • #
  • #
  • #
  • #
  • #
  • #
  • #
  • #
  • #
  • #
  • #

Унифицированный язык моделирования ( UML ) — это язык моделирования общего назначения в области разработки программного обеспечения, предназначенный для обеспечения стандартного подхода к визуализации проектирования системы. Первоначальным мотивом создания UML было желание стандартизировать различные системы обозначений и методы проектирования программного обеспечения. В UML диаграмма классов является одним из шести типов структурных диаграмм . Диаграммы классов являются основой процесса моделирования объектов и моделируют статическую структуру системы.

Структурные диаграммы показывают статическую структуру системы и ее частей на разных уровнях абстракции и реализации и то, как они связаны друг с другом. Элементы на структурной диаграмме представляют значимые концепции системы и могут включать абстрактные концепции, концепции реального мира и реализации. Существует семь следующих типов структурных диаграмм:

  • Диаграмма классов
  • Диаграмма компонентов
  • Схема развертывания
  • Диаграмма объекта
  • Схема пакета
  • Схема составной структуры
  • Диаграмма профиля

Обзор 14 типов диаграмм UML

Что такое диаграмма классов?

Диаграмма классов на унифицированном языке моделирования (UML) — это диаграмма статической структуры, которая описывает структуру системы, показывая ее классы, их атрибуты, операции (или методы) и отношения между объектами. Диаграмма классов — это план системы или подсистемы. Диаграммы классов можно использовать для моделирования объектов, составляющих систему, для демонстрации отношений между объектами и для описания ролей этих объектов и предоставляемых ими услуг.

Цель UML — предоставить стандартную нотацию, которую можно использовать всеми объектно-ориентированными методами, а также выбрать и интегрировать лучшие элементы предшествующих нотаций. UML был разработан для широкого круга приложений. Следовательно, он предоставляет конструкции для широкого спектра систем и действий (например, распределенные системы, анализ, проектирование и развертывание систем).

UML — это нотация, возникшая в результате объединения OMT с

  1. Техника объектного моделирования OMT  [ James Rumbaugh  1991] – лучше всего подходит для анализа и информационных систем с интенсивным использованием данных.
  2. Booch [ Gradi Booch  1994] — отлично подходил для дизайна и реализации. Грэди Буч много работал с  языком Ада  и был главным игроком в разработке объектно-ориентированных методов для этого языка. Хотя метод Буча был сильным, нотация была принята хуже (в его моделях преобладало множество форм облаков — не очень аккуратно).
  3. OOSE (Object-Oriented Software Engineering [ Ivar Jacobson  1992]) — включает модель, известную как варианты использования. Сценарии использования — это мощная техника для понимания поведения всей системы (область, в которой объектно-ориентированный подход традиционно был слабым).

В 1994 году Джим Рамбо, создатель OMT, ошеломил мир программного обеспечения, когда он покинул General Electric и присоединился к Грэди Бучу в Rational Corp. Целью партнерства было объединить их идеи в единый унифицированный метод (рабочее название метод действительно был «унифицированным методом»).

История UML

Назначение диаграммы классов

Диаграммы классов полезны на многих этапах проектирования системы. На этапе анализа диаграммы классов могут помочь вам понять требования предметной области и определить ее компоненты. В проектах объектно-ориентированного программного обеспечения диаграмма классов, созданная на ранних стадиях проекта, содержит классы, которые часто преобразуются в реальные классы и объекты программного обеспечения при написании кода.

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

Диаграммы классов широко используются при моделировании объектно-ориентированных систем, поскольку они являются единственными диаграммами UML, которые могут быть отображены непосредственно на объектно-ориентированные языки. На этапе реализации цикла разработки программного обеспечения вы можете использовать диаграммы классов для преобразования моделей в код и кода в модели.

Пример класса

У собаки есть состояния – цвет, имя, порода, а также поведение – виляние, лай, еда. Объект является экземпляром класса.

Обозначение класса UML

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

Имя класса:

  • Имя класса появляется в первом разделе.

Атрибуты класса:

  • Атрибуты показаны во втором разделе.
  • Тип атрибута отображается после двоеточия.
  • Атрибуты сопоставляются с переменными-членами (элементами данных) в коде.

Классовые операции (методы):

  • Операции показаны в третьем разделе. Это услуги, предоставляемые классом.
  • Тип возвращаемого значения метода отображается после двоеточия в конце подписи метода.
  • Возвращаемый тип параметров метода отображается после двоеточия после имени параметра. Операции сопоставляются с методами класса в коде

Классовые отношения

Класс может быть вовлечен в одно или несколько отношений с другими классами. Связь может быть одного из следующих типов: (См. рисунок справа для графического представления отношений).

Имена отношений

  • Имена отношений пишутся посередине ассоциативной строки.
  • Названия хороших отношений имеют смысл, если прочитать их вслух:
    • «Каждая электронная таблица  содержит  некоторое количество ячеек»,
    • «выражение  оценивается  как значение»
  • Они часто имеют  небольшую стрелку, указывающую направление,  в котором следует читать отношения, например, выражения оцениваются как значения, но значения не оцениваются как выражения.

Отношения — Роли

  • Роль – это направленная цель ассоциации.
  • Роли написаны на концах ассоциативной линии и описывают цель, которую играет этот класс в отношениях.
    • Например, ячейка связана с выражением. Характер связи таков, что выражением является  формула  клетки.

Судоходность

Стрелки указывают, возможно ли при наличии одного экземпляра, участвующего в отношении, определить связанные с ним экземпляры другого класса.

Диаграмма выше предполагает, что,

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

Видимость атрибутов класса и операций

В объектно-ориентированном дизайне есть нотация видимости атрибутов и операций. UML определяет четыре типа видимости:  public ,  protected ,  private и  package .

Символы +, -, # и ~ перед именем атрибута и операции в классе обозначают видимость атрибута и операции.

  • + обозначает общедоступные атрибуты или операции
  • – обозначает частные атрибуты или операции
  • # обозначает защищенные атрибуты или операции
  • ~ обозначает атрибуты пакета или операции

Пример видимости класса

В приведенном выше примере:

  • attribute1 и op1 MyClassName являются общедоступными
  • attribute3 и op3 защищены.
  • attribute2 и op2 являются частными.

Доступ для каждого из этих типов видимости показан ниже для членов разных классов.

Право доступа общественный (+) частный (-) защищенный (#) Пакет (~)
Члены одного класса да да да да
Члены производных классов да нет да да
Члены любого другого класса да нет нет в том же пакете

Множественность

Сколько объектов каждого класса принимают участие в отношениях и множественности можно выразить как:

  • Ровно один — 1
  • Ноль или единица – 0..1
  • Многие – 0..* или *
  • Один или несколько – 1..*
  • Точное число — например, 3..4 или 6
  • Или сложное отношение — например, 0..1, 3..4, 6.* будет означать любое количество объектов, кроме 2 или 5.

Пример множественности

  • Требование: Студент может посещать несколько курсов, и многие студенты могут быть зачислены на один курс.
  • В приведенном ниже примере  диаграмма классов  (слева) описывает формулировку вышеуказанного требования для статической модели, а диаграмма объектов (справа) показывает моментальный снимок (экземпляр диаграммы классов) зачисления на курс для курсы по программной инженерии и управлению базами данных соответственно)

Диаграмма объекта

Пример агрегации — компьютер и комплектующие

  • Агрегация — это частный случай ассоциации, обозначающий иерархию «состоит из».
  • Агрегат — это родительский класс, компоненты — это дочерние классы.

Пример агрегации

Пример наследования — таксономия ячеек

  • Наследование — еще один частный случай ассоциации, обозначающей «своего рода» иерархию.
  • Наследование упрощает модель анализа, вводя таксономию
  • Дочерние классы наследуют атрибуты и операции родительского класса.

Пример наследования

Диаграмма классов — пример инструмента диаграммы

Диаграмма классов может также иметь примечания, прикрепленные к классам или отношениям. Примечания отображаются серым цветом.

Пример диаграммы классов

В приведенном выше примере:

Мы можем интерпретировать значение приведенной выше диаграммы классов, прочитав пункты следующим образом.

  1. Форма — это абстрактный класс. Он показан курсивом.
  2. Форма — это суперкласс. Круг, прямоугольник и многоугольник являются производными от формы. Другими словами, Круг — это Форма. Это отношение обобщения/наследования.
  3. Существует связь между DialogBox и DataController.
  4. Форма является частью окна. Это агрегационные отношения. Shape может существовать без Window.
  5. Точка является частью Круга. Это композиционные отношения. Точка не может существовать без Окружности.
  6. Окно зависит от события. Однако Event не зависит от Window.
  7. Атрибутами Circle являются радиус и центр. Это класс сущности.
  8. Имена методов Circle: area(),circ(), setCenter() и setRadius().
  9. Радиус параметра в Circle является параметром in типа float.
  10. Метод area() класса Circle возвращает значение типа double.
  11. Атрибуты и имена методов Rectangle скрыты. У некоторых других классов на диаграмме также скрыты атрибуты и имена методов.

Пример диаграммы классов: система заказов

Пример диаграммы классов: система заказов

Пример диаграммы классов: графический интерфейс

Диаграмма классов может также иметь примечания, прикрепленные к классам или отношениям.

Пример диаграммы классов: графический интерфейс

Работа со сложной системой — диаграмма нескольких или одного класса?

Неизбежно, если вы моделируете большую систему или большую бизнес-сферу, вам придется учитывать множество сущностей. Должны ли мы использовать несколько или одну диаграмму классов для моделирования проблемы? Ответ:

  • Вместо моделирования каждой сущности и ее взаимосвязей на одной диаграмме классов лучше использовать несколько диаграмм классов.
  • Разделение системы на несколько диаграмм классов облегчает понимание системы, особенно если каждая диаграмма является графическим представлением определенной части системы.

Перспективы диаграммы классов в жизненном цикле разработки программного обеспечения

Мы можем использовать диаграммы классов на разных этапах  жизненного цикла разработки программного обеспечения  и, как правило, моделируя диаграммы классов в трех различных перспективах (уровнях детализации) постепенно по мере продвижения вперед:

Концептуальная перспектива : диаграммы интерпретируются как описание вещей в реальном мире. Таким образом, если вы принимаете концептуальную перспективу, вы рисуете диаграмму, которая представляет концепции в изучаемой области. Эти концепции будут естественным образом относиться к классам, которые их реализуют. Концептуальная перспектива  считается независимой от языка .

Перспектива спецификации : диаграммы интерпретируются как описание программных абстракций или компонентов со спецификациями и интерфейсами, но без привязки к конкретной реализации. Таким образом, если вы смотрите на спецификацию, мы  смотрим на интерфейсы программного обеспечения , а не на реализацию.

Перспектива реализации : диаграммы интерпретируются как описание программных реализаций на определенной технологии и  языке . Таким образом, если вы смотрите на реализацию, мы  смотрим на программную реализацию .

Ищете бесплатный инструмент для построения диаграмм классов?

Visual Paradigm Online (VP Online) Free Edition — это бесплатное онлайн-программное обеспечение для рисования, которое поддерживает диаграммы классов, другие диаграммы UML, инструменты ERD и инструменты организационных диаграмм. Он имеет простой, но мощный редактор, который позволяет быстро и легко создавать диаграммы классов. В этом бесплатном редакторе UML нет рекламы, нет сроков доступа и нет ограничений, например, на количество диаграмм, количество фигур и т. д. Вы являетесь владельцем диаграмм, которые создаете для личных и некоммерческих целей.

Онлайн-инструмент для построения диаграмм классов

Ищете более формальное моделирование UML на рабочем столе?

Visual Paradigm Community Edition был запущен в 2004 году для предоставления  бесплатного программного обеспечения UML  исключительно для некоммерческих целей, поддержки пользователей, которые делали свои первые шаги в моделировании UML и которым требуется бесплатное и кросс-платформенное программное обеспечение для моделирования UML для личного использования, например как применение UML в студенческих проектах.

Экран визуальной парадигмы

Бесплатный инструмент моделирования UML для всех видов некоммерческих целей. Поддержка 13 диаграмм UML 2.x

Бесплатный инструмент UML с поддержкой 13 диаграмм UML 2.x

Нас приняли более 1 миллиона установок по всему миру, и эта цифра продолжает расти. Многие люди используют платные версии Visual Paradigm для ежедневного рисования профессиональных диаграмм UML и ERD для проектирования и анализа систем и баз данных.

Причина 2

Доверие ИТ-специалистов и крупных организаций

Многие крупные организации, ИТ-компании, консультанты, университеты, неправительственные организации и правительственные учреждения по всему миру приняли Visual Paradigm (платные версии). На рисунке ниже показаны некоторые из наших платных клиентов.

Клиенты визуальной парадигмы

Причина 3

Высокое качество – награда

Нам доверяют не только самые известные предприятия мира, но и вся отрасль. Visual Paradigm — многократный обладатель международных наград.

Награды визуальной парадигмы

Причина 4

Самая широко используемая платформа для моделирования в академических кругах.

Наиболее широко используемый инструмент UML для академических кругов, принятый тысячами университетов и колледжей.

Школы, использующие визуальную парадигму

Причина 5

Сотни примеров и шаблонов диаграмм UML и ERD

Сотни примеров UML и ERD,  готовых для импорта в Visual Paradigm для мгновенного эксперимента или для начала работы с собственной моделью UML. Все бесплатно.

Причина 6

Путь обновления до коммерческих выпусков для широкого спектра приложений и возможностей

Простое обновление для огромного набора дополнительных функций (например, BPMN и поддержки совместной работы) и для коммерческого использования, начиная с  6 долларов США в месяц .

Упакованные функции в Visual Paradigm

Причина 7

Форум активных пользователей для получения помощи и обмена идеями и опытом

Поддерживайте, делитесь и обменивайтесь своими идеями с другими людьми на  форуме активных пользователей Visual Paradigm .

Форум визуальной парадигмы

Причина 8

Кроссплатформенное, удобное, быстрое и отзывчивое приложение

Visual Paradigm может работать на разных платформах, таких как Windows, Linux и Mac. Его интуитивно понятный интерфейс и мощные функции моделирования делают моделирование быстрым и легким!

Кроссплатформенное программное обеспечение UML

использованная литература

  • Что такое УМЛ?
  • Почему UML-моделирование?
  • Обзор 14 типов диаграмм UML
  • Что такое диаграмма классов?
  • Что такое диаграмма компонентов?
  • Что такое диаграмма развертывания?
  • Что такое диаграмма объекта?
  • Что такое пакетная диаграмма?
  • Что такое составная структурная диаграмма?
  • Что такое профильная диаграмма?
  • Что такое диаграмма вариантов использования?
  • Что такое Диаграмма активности?
  • Что такое диаграмма состояний?
  • Что такое диаграмма последовательности?
  • Что такое коммуникационная диаграмма?
  • Что такое обзорная диаграмма взаимодействия?
  • Что такое временная диаграмма

Сущности на диаграммах классов связываются главным образом отношениями ассоциации (в том числе агрегирования
и композиции) и обобщения. Отношения зависимости и реализации на диаграммах классов применяются реже, но,
тем не менее, они также применяются, и мы начнем с них, как с более простых.

Всего в UML определено довольно большое количество стандартных стереотипов отношения зависимости, которые
можно разделить на несколько групп:

  • между классами и объектами на диаграмме классов;
  • между пакетами;
  • между вариантами использования;
  • другие.

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

Табл. Стандартные стереотипы зависимостей на диаграмме классов

Стереотип Описание
«bind» Подстановка параметров в шаблон. Независимой сущностью является шаблон (класс с параметрами), а
зависимой ‒ класс, который получается из шаблона заданием аргументов.
«call» Указывает зависимость между двумя операциями: операция зависимого класса вызывает операцию
независимого класса.
«derive» Буквально означает «может быть вычислен по». Зависимость с данным стереотипом применяется не
только к классам, но и к другим элементам модели: атрибутам, ассоциациям и т.д. Суть состоит в
том, зависимый элемент может быть восстановлен по информации, содержащейся в независимом
элементе. Таким образом, данная зависимость показывает, что зависимый элемент, вообще говоря,
излишен и введен в модель из соображений удобства, наглядности и т.д.
«friend» Назначает специальные права видимости. Зависимый класс имеет доступ к составляющим независимого
класса, даже если по общим правилам видимости такие права у него отсутствуют.
«instanceOf» Указывает, что зависимый объект (или класс) является экземпляром независимого класса
(метакласса).
«instantiate» Указывает, что операции зависимого класса создают экземпляры независимого класса.
«powertype» Показывает, что экземплярами зависимого класса являются подклассы независимого класса. Таким
образом, в данном случае зависимый класс является метаклассом.
«refine» Указывает, что зависимый класс уточняет (конкретизирует) независимый. Данная зависимость
показывает, что связанные классы концептуально совпадают, но находятся на разных уровнях
абстракции.
«use» Зависимость самого общего вида, показывающая, что зависимый класс каким-либо образом использует
независимый класс.

Повторим еще раз, что зависимости на диаграммах классов используются сравнительно редко, потому что имеют
более расплывчатую семантику по сравнению с ассоциациями и обобщением.

Рассмотрим отношение реализации. Между интерфейсами и другими классификаторами, в частности, классами, на
диаграмме классов применяются два отношения:

  • классификатор (в частности, класс) использует интерфейс ‒ это показывается с помощью
    зависимости со стереотипом «call»;
  • классификатор (в частности, класс) реализует интерфейс ‒ это показывается с помощью отношения
    реализации.

Никаких ограничений на использование отношения реализации не накладывается: класс может реализовывать
много интерфейсов, и наоборот, интерфейс может быть реализован многими классами. Нет ограничений и на
использование зависимостей со стереотипом «call» ‒ класс может вызывать
любые операции любых видимых интерфейсов. Семантика зависимости со стереотипом «call»
очень проста ‒ эта зависимость указывает, что в операциях класса, находящегося на независимом
полюсе, вызываются операции класса (в частности, интерфейса) находящегося на зависимом полюсе.

Рассмотрим пример из информационной системы отдела кадров. Допустим, что класс Department
для реализации операций связанных с движением кадров, использует операции класса Position, позволяющие занимать и освобождать должность ‒ другие
операции класса Position классу Department
не нужны. Для этого, как показано на рисунке можно определить соответствующий интерфейс IPosition и связать его отношениями с данными классами.

Отношения реализации и использования интерфейсов

Рис. Отношения реализации и использования интерфейсов

Используя нотацию «чупа-чупс», появившуюся в UML 2, эту же модель можно изобразить лаконично,
симметрично и просто, как показано ниже.

Использование нотации 'чупа-чупс'

Рис. Использование нотации «чупа-чупс»

Отношение обобщения (параграф 1.4.2) часто применяется
на диаграмме классов. Действительно, трудно представить себе ситуацию, когда между классами в одной
системе нет ничего общего. Как правило, общее есть, и это общее целесообразно выделить в отдельный
класс. При этом общие составляющие, собранные в суперклассе, автоматически наследуются подклассами.

Таким образом, сокращается суммарное количество описаний, а значит, уменьшается вероятность допустить
ошибку. Использование обобщений не ограничивает свободу проектировщика системы, поскольку
унаследованные составляющие можно переопределить в подклассе. Для указания того, что та или иная
составляющая переопределена в подклассе следует использовать появившееся в UML 2 дополнение redefines, о котором мы поговорим ниже.

При обобщении выполняется принцип подстановки, что фактически означает увеличение
гибкости и универсальности программного кода, при одновременном сохранении надежности,
обеспечиваемой контролем типов. Действительно, если, например, в качестве типа параметра некоторой
процедуры указать суперкласс, то процедура будет с равным успехом работать в случае, когда в
качестве аргумента ей передан объект любого подкласса данного суперкласса. Суперкласс может быть
конкретным, идентифицированным одним из методов, описанных в параграфе
3.1.4, а может быть абстрактным, введенным именно для построения отношений обобщения.

Принцип подстановки, сформулированный Барбарой Лисков (Liskov substitution principle)
заключается в том, что экземпляр подкласса может использоваться везде, где используется
экземпляр его суперкласса, поддерживая таким образом контракт, предлагаемый суперклассом.
Другими словами, подкласс обеспечивает тот же интерфейс что и суперкласс, т.е. происходит
открытое наследование интерфейса.

По умолчанию обобщения являются подстановочными (substitutable), т.е. удовлетворяют принципу
подстановки. Однако в UML существуют и неподстановочные обобщения, для которых соответственно,
принцип подстановки не выполняется. Никакой специальной нотации для таких обобщений не
предусмотрено, однако инструменты должны уметь поддерживать данное свойство отношения обобщения,
которое называется isSubstituable.

Рассмотрим пример.

ИЗМЕНЕНИЯ В ТЕХНИЧЕСКОМ ЗАДАНИИ

Каждая структурная единица предприятия (подразделение, должность) должна иметь свое название.

В информационной системе отдела кадров мы выделили классы Position,
Department и Person (см. параграф 3.1.4). Для всех этих классов может быть указан атрибут,
содержащий собственное имя объекта, выделяющее его в ряду однородных. Для простоты положим, что
такой атрибут имеет тип String. В таком случае можно определить
суперкласс, ответственный за хранение данного атрибута и работу с ним, а прочие классы связать с
суперклассом отношением обобщения. Однако более пристальный анализ предметной области наводит на
мысль, что работа с собственным именем для выделенных классов производится не совсем одинаково.
Действительно, назначение и изменение собственных имен подразделениям и должностям находится в
пределах ответственности информационной системы отдела кадров, но назначение (тем паче изменение)
собственного имени сотрудника явно выходит за эти пределы. Исходя из этих соображений, мы приходим к
структуре обобщений, представленной ниже.

Отношение обобщения

Рис. Отношение обобщения

Операция setName(), объявленная в классе Unit переопределена для класса Person. На это указывает заключенное в фигурные скобки, и следующее за
определением операции, дополнение redefines 1. Переопределение состоит в том, что значение видимости для операции
setName() изменено с «открытая» на «закрытая».

Обратите внимание, что суперкласс Unit определен как
абстрактный, т.е. не имеющий возможность иметь непосредственные экземпляры, поскольку в системе не
предполагается иметь объекты данного класса. Экземпляры существуют для конкретных подклассов Department, Position и Person. Класс Unit в данном
случае нужен только для того, чтобы свести описания одного атрибута и двух операций в одном месте и
не повторять их дважды.

В объектно-ориентированном программировании распространена идиома, согласно которой корневые
интерфейсы (классы) иерархий определяют абстрактную операцию, переопределяемую в подклассах, которая
во время исполнения возвращает некоторую информацию о конкретном экземпляре созданного подкласса.
Данную информацию можно использовать, например, для тестирования или в операциях безопасного
приведения типов.

Мы также введем подобную операцию в абстрактном классе Unit,
преследую при этом только одну цель ‒ продемонстрировать читателю отношение между понятиями
абстрактная операция 2 и операция с методом 3, рассмотренными в параграфе 3.2.3.

Отношения обобщения можно задавать в UML довольно свободно, но не совсем произвольно. Обобщения
в модели должны образовывать строгий частичный порядок.

Таким образом, в UML допускается, чтобы класс был подклассом нескольких суперклассов (множественное
наследование), не требуется, чтобы у базовых классов был общий суперкласс (несколько иерархий
обобщения) и вообще не накладывается никаких ограничений, кроме частичной упорядоченности (т.е.
отсутствия циклов в цепочках обобщений). Нарушение данного условия является синтаксической ошибкой,
однако не все инструменты проверяют это условие ‒ цикл может быть незаметен, потому что
отдельные дуги цикла обобщений могут быть показаны на разных диаграммах.

При множественном наследовании (multiple inheritance) возможны конфликты: суперклассы
содержат составляющие, которые невозможно включить в один подкласс, например, атрибуты с одинаковыми
именами, но разными типами. В UML конфликты при множественном наследовании считаются нарушением
правил непротиворечивости модели (параграф 1.8.2). Если же конфликты
отсутствуют, то множественное наследование в UML не только не запрещается, но даже поощряется! В
частности, метамодели в стандарте изобилуют примерами множественного наследования.

Несмотря на те сложности, которые существуют при использовании и в реализации поддержки
множественного наследования, его не стоит бояться. Как заметил один из авторов UML (Гради
Буч) «проблема множественного наследования находится в головах у архитекторов, а не в языке
моделирования».

В UML существует возможность выделять в множестве обобщений подмножества обобщений
(generalization set) и задавать ограничения для них.

Рассмотрим следующий (несколько искусственный) пример для информационной системы отдела кадров.
Допустим, что для экземпляров класса Person требуется смоделировать
такие характеристики, как пол ‒ Gender и служебное положение ‒
Employment Status. Подклассы Male
Person
и Female Person будут описывать пол сотрудника, а
Employer и Employee, соответственно,
его служебное положение. Тогда данную ситуацию можно описать с помощью следующей диаграммы.

Подмножества обобщений

Рис. Подмножества обобщений

Классификация по полу является завершенной и дизъюнктной. Классификация по служебному положению,
напротив, не является ни завершенной, ни дизъюнктной.

Имена подмножеств обобщений, указываются после двоеточия рядом с обобщением, а в случае, если надо
охватить несколько обобщений, то применяется нотация в виде пунктирной линии, как это сделано для
подмножества Employment Status. Помимо имени подмножества можно указать
накладываемые на подмножество ограничения (или их комбинацию). Возможные варианты ограничений
приведены в следующей таблице.

Табл. Ограничения на подмножество обобщений

Ограничение Применение
{complete}
полнота
Множество обобщений, входящих в подмножество, является полным, т.е. определяет все возможные
подтипы для данной характеристики суперклассификатора. Каждый экземпляр суперклассификатора
должен быть экземпляром какого-либо подклассификатора.
{incomplete}
неполнота
Множество обобщений, входящих в подмножество, не является полным, т.е. определяет только
часть возможных подклассификаторов для данной характеристики суперклассификатора. Некоторый
экземпляр суперклассификатора может не являться экземпляром ни одного подклассификатора из
множества.
{disjoint}
несовместность
Области значений подклассификаторов, входящих в данное подмножество не пересекаются, т.е.
являются взаимоисключающими. У них не может быть общего прямого или косвенного экземпляра.
{overlapping}
совместность
Области значений подклассификаторов могут пересекаться, т.е. они не являются
взаимоисключающими. У них может быть общий прямой или косвенный экземпляр.

Как видно из описания, приведенного выше, пары {compete} ‒ {incomplete} и {disjoint} ‒ {overlapping} являются взаимоисключающими, т.е. не может быть одновременно
множество и {complete}, и {incomplete}.
Значения ограничений по умолчанию ‒ {incomplete, disjoint}.

Перед тем как приступить к описанию наиболее часто используемого отношения ‒ отношения
ассоциации, подведем итог сказанному с помощью диаграммы метамодели отношений зависимости,
реализации и обобщения.

Метамодель отношений зависимости, реализации и обобщения

Рис. Метамодель отношений зависимости, реализации и обобщения

Отношение ассоциации является, видимо, самым важным на диаграмме классов. В общем случае
ассоциация, нотация которой ‒ сплошная линия, соединяющая классы, означает, что экземпляры
одного класса связаны с экземплярами другого класса
. Поскольку экземпляров может быть
много, и каждый может быть связан с несколькими, ясно, что ассоциация является дескриптором, который
описывает множество наборов связанных объектов. В UML ассоциация является классификатором,
экземпляры которого называются связями.

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

Связь между объектами (экземплярами классов) в программе может быть организована самыми разными
способами. Например, в объекте одного класса может храниться указатель или ссылка на объект другого
класса. Связь не обязательно является непосредственно хранимым физическим адресом. Этот адрес может
динамически вычисляться во время выполнения программы на основании другой информации. Например, если
объекты представлены как записи в таблице базы данных, то связь означает, что в записи одного
объекта имеется поле, значением которого является первичный ключ записи другого объекта (из другой
таблицы). Еще пример: использование какого-либо механизма динамического связывания по имени
(уникальному идентификатору) объекта. При моделировании на UML техника реализации связи между
объектами не имеет значения. Ассоциация в UML подразумевает лишь то, что связанные объекты обладают
достаточной информацией для организации взаимодействия. Возможность взаимодействия означает, что
объект одного класса может послать сообщение объекту другого класса, в частности, вызвать метод или
же прочитать или изменить значение открытого атрибута. Поскольку в объектно-ориентированной
программе такого рода действия и составляют суть выполнения программы, моделирование структуры
взаимосвязей классов (т.е. выявление ассоциаций) является одной из ключевых задач моделирования.

Как уже было сказано, базовая нотация ассоциации (сплошная линия) позволяет указать, что объекты
ассоциированных классов могут взаимодействовать во время выполнения. Но это только малая часть того,
что можно моделировать с помощью отношения ассоциации. Для ассоциации в UML предусмотрено наибольшее
количество различных дополнений, которые мы сначала перечислим, а потом рассмотрим по порядку.
Дополнения, как обычно, не являются обязательными: их используют при необходимости, в различных
ситуациях по-разному. Если использовать все дополнения сразу, то диаграмма становится настолько
перегруженной, что ее трудно читать. Итак, для ассоциации определены следующие дополнения:

  • имя ассоциации (возможно, вместе с направлением чтения);
  • кратность полюса ассоциации;
  • агрегации или композиция;
  • возможность навигации для полюса ассоциации;
  • роль полюса ассоциации;
  • видимость полюса ассоциации;
  • упорядоченность объектов на полюсе ассоциации;
  • изменяемость множества объектов на полюсе ассоциации;
  • ограничения subset и union
    полюса ассоциации;
  • класс ассоциации;
  • квалификатор полюса ассоциации;
  • переопределение полюса ассоциации.

Рассмотрим их по порядку.

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

Например, в информационной системе отдела кадров, если сотрудник занимает должность, то
соответствующие экземпляры классов Person и Position
должны быть связаны, т.е. между самими классами должно быть отношение ассоциации 1 и может быть имя 2, поясняющее ее
назначение. Дополнительно можно указать направление чтения имени ассоциации 3. Фрагмент графической модели, приведенный ниже, фактически можно
прочитать вслух: Person occupies Position.

Имя ассоциации и направление чтения

Рис. Имя ассоциации и направление чтения

Кратность полюса ассоциации указывает, сколько объектов данного класса (со стороны данного
полюса) участвуют в связи. Кратность может быть задана как конкретное число, и тогда в каждой связи
со стороны данного полюса участвует ровно столько объектов, сколько указано. Более распространен
случай, когда кратность указывается как диапазон возможных значений, и тогда число объектов,
участвующих в связи должно находиться в пределах указанного диапазона. При указании кратности можно
использовать символ *, который обозначает неопределенное число (см.
табл. Выражения кратности в параграфе 3.1.3). Например, если в
информационной системе отдела кадров не предусматривается дробление ставок и совмещение должностей,
то работающему сотруднику соответствует одна должность 1, а
должности соответствует один сотрудник или ни одного 2, то есть
должность вакантна. Ниже приведен соответствующий фрагмент диаграммы UML.

Кратность полюсов ассоциации

Рис. Кратность полюсов ассоциации

Более сложные случаи также легко моделируются с помощью кратности полюсов. Например, если мы хотим
предусмотреть совмещение должностей и хранить информацию даже о неработающих сотрудниках, то
диаграмма примет вид, приведенный ниже (запись * эквивалентна записи
0..*).

Использование неопределенной кратности

Рис. Использование неопределенной кратности

В UML используются два частных, но очень важных случая отношения ассоциации, которые называются
агрегацией и композицией. В обоих случаях речь идет о моделировании отношения типа «часть ‒
целое». Ясно, что отношения такого типа следует отнести к отношениям ассоциации, поскольку части и
целое обычно взаимодействуют.

Агрегация (aggregation) ‒ это ассоциация между классом A (часть) и классом B (целое),
которая означает, что экземпляры (один или несколько) класса A
входят в состав экземпляра класса B.

Это отмечается с помощью специального графического дополнения: на полюсе ассоциации, присоединенному
к «целому», изображается незакрашенный ромб 1. Например, на следующем
рисунке указано, что сотрудник является членом рабочей группы Workgroup.

Отношение агрегации

Рис. Отношение агрегации

При этом никаких дополнительных ограничений не накладывается: экземпляр класса Person (часть) может быть связан с другими объектами (т.е. класс Person может участвовать в нескольких агрегациях), создаваться и
уничтожаться независимо от экземпляров класса Workgroup (целого).

Композиция (composition) ‒ это ассоциация между классом A (часть) и классом B (целое),
которая дополнительно накладывает более сильные ограничения в сравнении с агрегацией:
композиционно часть A может входить только в одно целое B, часть существует, только пока существует целое и прекращает
свое существование вместе с целым.

Однако часть может быть отделена от целого до того, как оно будет удалено. В указанном случае
композиция будет разрушена.

Графически отношение композиции отображается закрашенным ромбом 1. Для примера далее на рисунке приведен
еще один взгляд на отношения между рабочими группами и сотрудниками в информационной системе отдела
кадров. В этом случае, мы считаем, что в организации принята жесткая («армейская») структура: каждый
сотрудник входит ровно в одну рабочую группу и в каждой рабочей группе есть по меньшей мере один
сотрудник. Для моделирования такой структуры используется композиция. Если же структура более
аморфна: возможны «висящие в воздухе» сотрудники, бывают «пустые» рабочие группы и т.д., то для
моделирования такой структуры более адекватным средством является агрегация (см. предыдущий
рисунок).

Отношение композиции

Рис. Отношение композиции

При использовании отношения композиция, «целое» часто называют композитом, а при
использовании агрегации ‒ агрегатом.

Понятиям агрегации и композиции можно также дать не совсем универсальную и точную, но понятную
программистскую интерпретацию. Допустим, что в классе Workgroup
имеется атрибут класса Person. В этом случае естественно считать, что
экземпляр класса Person является частью экземпляра класса Workgroup. Если экземпляр класса Workgroup не владеет экземпляром класса Person,
т.е. при реализации используется указатель или ссылка, то это агрегация, а если значением атрибута
является непосредственно экземпляр класса Person, то это композиция.

В комбинации с указанием кратности, отношения ассоциации, агрегации и композиции позволяют лаконично
и полно отобразить структуру классов: что из чего состоит и как связано. Здесь приведен пример
одного из вариантов такой структуры для информационной системы отдела кадров.

Структура связей классов информационной системы отдела кадров

Рис. Структура связей классов информационной системы отдела кадров

Обратите внимание на ассоциацию с именем works for 1
на рисунке выше. Это производная ассоциация.

Производный элемент (derived element) ‒ это элемент, который можно
вычислить или определить по другим элементам.

Формально производный элемент излишен, он вводится в модель для ясности или наглядности. Производный
элемент отмечается с помощью знака /, который ставится перед его именем.
Производным элементом в UML может быть атрибут, роль полюса или ассоциация.

Сделаем еще два важных замечания относительно композиции.

Первое касается соотношения понятия атрибута и понятия композиции. Действительно, если атрибут типа
Part входит в класс Composite, то это
фактически означает композицию, где Composite ‒ целое, а Part ‒ часть. В каких случаях при моделировании следует
использовать атрибуты, а в каких ‒ композицию? Ответ на это вопрос зависит от того, какие еще
отношения между составными частями нужно определить. Если никаких отношений нет, например, если речь
идет о частях, которые имеют примитивные типы (числа, строки, и тому подобное), то следует
использовать атрибуты. Если же между частями есть отношения, в частности, есть взаимодействие, то
предпочтительнее использовать композицию, потому что нотация атрибутов не дает возможности отразить
эти отношения.

Рассмотрим пример.

ИЗМЕНЕНИЯ В ТЕХНИЧЕСКОМ ЗАДАНИИ

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

Простейшим и, как следствие, не самым лучшим, является решение, представленное ниже.

Пример использования атрибутов

Рис. Пример использования атрибутов

Оптимальное решение, которое позволяет легко учесть новое требование, приведено на следующем рисунке.

Пример использования композиции

Рис. Пример использования композиции

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

Рассмотрим пример.

ИЗМЕНЕНИЯ В ТЕХНИЧЕСКОМ ЗАДАНИИ

В подразделении любого уровня, в том числе и на предприятии в целом, имеется единственная должность (начальник), которую система должна трактовать особым образом.

При реализации данного требования (см. следующий рисунок) первым побуждением является ввести новый
класс Boss (подкласс класса Position)
и провести композиции к классам Company 1 и Department 2. Как ни странно может показаться, но это не является синтаксической
ошибкой. В этом случае речь пойдет о том, что принадлежащий экземпляру класса Company экземпляр класса Boss не
может в то же самое время быть частью какого-либо экземпляра класса Department
и наоборот (но самих экземпляров класса Boss может быть несколько).

Первый вариант реализации сложной композиции

Рис. Первый вариант реализации сложной композиции

Второй вариант решения состоит в следующем: если у группы классов есть нечто общее, то можно завести
абстрактный суперкласс (класс UnitWithBoss) и установить
требуемую композицию с ним.

Второй вариант реализации сложной композиции

Рис. Второй вариант реализации сложной композиции

Третий вариант, показанный на следующем рисунке, использует еще одно средство UML ‒ обобщение
ассоциации
(association generalization). Класс Boss участвует не
в трех композициях, как может показаться на первый взгляд, а в одной, с абстрактным классом Unit 1. Две другие композиции к
классам Company и Department, не
являются независимыми отношениями, они являются частными случаями одного отношения композиции, что
показано стрелками обобщения 2 и 3.

Третий вариант реализации сложной композиции

Рис. Третий вариант реализации сложной композиции

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

На следующем рисунке роль полюса класса Boss в ассоциации с Unit носит имя Leader. Специализации
данной ассоциации 1 не только переопределяют имя данной роли (CEO и Top Manager), но также и добавляют
тип (INoReport и IReport, соответственно).

Использование redefines для полюсов ассоциации

Рис. Использование redefines для полюсов ассоциации

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

Роль (role) ‒ это интерфейс, который предоставляет классификатор в данной
ассоциации.

Мы надеемся, что туман неясности по поводу понятия «роль» в UML, порожденный нашими постоянными, но
неизбежными забеганиями вперед, теперь полностью развеялся.

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

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

Нотация этого дополнения ‒ текст, указанный на полюсе ассоциации. В общем случае роль полюса
ассоциации имеет следующий синтаксис:

видимость ИМЯ : тип

Имя является обязательным, оно называется именем роли и фактически является собственным именем полюса
ассоциации, позволяющим различать полюса. Если рассматривается одна ассоциация, соединяющая два
различных класса, то в именах ролей нет нужды: полюса ассоциации легко можно различить по именам
классов, к которым они присоединены. Однако, если это не так, т.е. если два класса соединены
несколькими ассоциациями, или же если ассоциация соединяет класс с самим собой, то указание ролей
полюсов ассоциации является необходимым. Такая ситуация отнюдь не является надуманной, как может
показаться и уже была продемонстрирована выше. Приведем еще один пример.

Описание иерархии должностей

Рис. Описание иерархии должностей

На рисунке изображена ассоциация класса Position с самим собой 1. На полюсах ассоциации указаны роли 2.
Значок, показывающий направление чтения 3 ‒ черный треугольник ‒
позволяет прочесть данную ассоциацию как Chief subordinates Subordinate.
Эта ассоциация призвана отразить наличие иерархии подчиненности должностей в организации. Однако на
приведенном рисунке видно только, что объекты класса Person образуют
некоторую иерархию (каждый объект связан с некоторым количеством нижележащих в иерархии объектов и
не более чем с одним вышележащим объектом), но не более того. Используя роли и, заодно, отношения
реализации, можно описать субординацию в информационной системе отдела кадров достаточно лаконично,
но точно. Например, на следующем ниже рисунке указано, что в иерархии субординации каждая должность
может играть две роли. С одной стороны, должность может рассматриваться как начальственная 1 (chief), и в этом случае она
предоставляет интерфейс IChief 2 имеющий операцию petition() (начальнику
можно подать служебную записку). С другой стороны, должность может рассматриваться как подчиненная
3 (subordinate), и в этом случае она
предоставляет интерфейс ISubordinate 4,
имеющий операцию report() (от подчиненного можно потребовать отчет). У
начальника может быть произвольное количество подчиненных 5, в том
числе и 0, у подчиненного может быть не более одного начальника 6.

Роли полюсов ассоциации

Рис. Роли полюсов ассоциации

Имея в виду ту же самую цель, можно поступить и по-другому: непосредственно включить в описание
класса составляющие, ответственные за обеспечение нужной функциональности (следующий рисунок).
Однако такое решение не самое лучшее: во-первых, оно слишком привязано к реализации, разработчику не
оставлено никакой свободы для творческих поисков эффективного решения; во-вторых оно менее наглядно,
потеряны информативные имена ролей и ассоциации; в-третьих, оно менее надежно ‒ в модели выше
подчиненный синтаксически не может потребовать отчета от начальника, а в модели ниже ‒ может, и
нужно предусматривать дополнительные средства для обработки этой ошибки.

Атрибуты и операции, обеспечивающие реализацию ролей

Рис. Атрибуты и операции, обеспечивающие реализацию ролей

Возможность навигации (navigability) для полюса ассоциации ‒ это свойство
полюса, имеющее значение типа Boolean, и определяющее, можно ли
эффективно получить с помощью данной ассоциации доступ к объектам класса, присоединенному к
данному полюсу ассоциации.

Если полюс не обладает данным свойством, то наличие эффективного (и вообще какого-либо) доступа к его
объектам не гарантируется. Для отображения факта возможности или не возможности навигации для
данного полюса ассоциации применяется следующая нотация: если навигация для некоторого полюса
возможна, то этот полюс отмечают стрелкой на конце линии ассоциации 1,
если же навигация не возможна, то на конце линии ассоциации рисуют косой крестик 2. В примере, приведенном ниже, навигация возможна только в
направлении от Company к Person, но не
наоборот.

Вариант использования направлений навигации

Рис. Вариант использования направлений навигации

В случае если ни один из элементов нотации описывающих свойство возможность навигации не присутствует
на конце линии ассоциации, то никакого предположения о значении этого свойства для данного полюса
сделать нельзя. Значение по умолчанию для него в UML отсутствует. В качестве примера можно
рассмотреть диаграмму Описание иерархии должностей.

Если ее не уточнить как показано ниже, то всевозможные предположения о значении данного свойства
могут иметь место.

Вариант использования направлений навигации

Рис. Вариант использования направлений навигации

Реальная практика использования свойства возможности навигации показывает, что помимо
явной интерпретации (описанной в стандарте), существует и неявная (часто используемая
инструментами и подразумеваемая в литературе), а именно, считается, что отсутствие стрелочек
на концах линии ассоциации говорит о возможности навигации.

Оставшуюся часть параграфа мы посвятим обсуждению многополюсных ассоциаций.

Сама по себе ассоциация между классами A и B ‒ это множество пар (a,b), где a ‒ экземпляр класса A, а b ‒ экземпляр класса B. Подчеркнем еще
раз, что это именно множество, так как двух одинаковых пар (a,b) быть не
может.

Чаще всего в моделях используются бинарные ассоциации, отражающие связи между объектами двух классов.
В UML определены также многополюсные ассоциации, отражающие связи между бóльшим числом объектов. С
формальной точки зрения многополюсные ассоциации излишни, поскольку их можно выразить через
комбинацию бинарных ассоциаций введением дополнительных сущностей. Действительно, упорядоченную
тройку объектов (a, b, c) ‒ элемент трехполюсной ассоциации ‒
можно представить как упорядоченную пару (a, d), где d ‒ новый объект, представляющий упорядоченную пару (b, c). Однако на практике (в некоторых случаях) многополюсные
ассоциации бывают буквально незаменимы. Рассмотрим следующий пример из информационной системы отдела
кадров.

ИЗМЕНЕНИЯ В ТЕХНИЧЕСКОМ ЗАДАНИИ

Информационная система должна поддерживать матричную структуру управления на предприятии и уметь оперировать таким понятием, как проект, в следующем контексте: один и тот же сотрудник может участвовать во многих проектах, выполняя различные обязанности (т.е. занимая различные должности).

В организации применяется современная организационная форма управления и помимо иерархии
подразделений и должностей существует структура выполняемых проектов, «пронизывающих» организацию. Такую замысловатую (но весьма
жизненную!) ситуацию очень легко отобразить, используя многополюсные ассоциации.

Многополюсная ассоциация

Рис. Многополюсная ассоциация

Остается сказать, что нотация многополюсной ассоциации представляет собой ромб 1,
к которому посредством линий присоединяются все классы, участвующие в ассоциации.

Чтобы полностью оценить полезность многополюсных ассоциаций, мы советуем читателям проделать весьма
поучительное упражнение: попытаться построить модель, семантически эквивалентную только что
приведенной, но без использования многополюсных ассоциаций (подсказка: придется ввести
дополнительные сущности и отношения).

Рассмотрим следующий пример из информационной системы отдела кадров. Три класса Person, Company и Department связаны следующими отношениями.

Фрагмент диаграммы классов ИС ОК

Рис. Фрагмент диаграммы классов ИС ОК

Ассоциации (и их варианты) указывают возможные пути навигации между экземплярами соответствующих
классов. Например, исходя из приведенного фрагмента модели, возможен такой маршрут: Person ‒ Company ‒ Department. Если рассматривать данный маршрут в проекции на
экземпляры классов, то получается следующее: имея экземпляр класса Person можно получить соответствующий экземпляр класса Company, т.е. узнать компанию, в которой работает данный сотрудник,
а затем, имея экземпляр класса Company, легко получить список всех
отделов, т.е. экземпляров класса Department.

ИЗМЕНЕНИЯ В ТЕХНИЧЕСКОМ ЗАДАНИИ

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

Другими словами, использование маршрута Person ‒ Company ‒ Department противоречит
техническому заданию. Действительно, из приведенного фрагмента модели следует, что потенциально
любой работник может получить полную информацию о структуре всего предприятия, на котором он
работает.

Рассмотрим маршрут Person ‒ Company ‒ Department еще раз.
Каждый их двух участков этого маршрута имеет право на существование. Действительно, работнику не
возбраняется знать, на кого он работает (ассоциация между Person и
Company), а компания должна иметь информацию о своей структуре
(композиция между Company и Department).
Получается, что навигация по отдельным участкам маршрута Person ‒
Company ‒ Department является
допустимой, в то время как на прохождение всего маршрута должно быть наложено ограничение, а точнее
говоря, запрет.

Выходом из подобных (не часто, но все-таки иногда возникающих) ситуаций служит использование
видимости полюса ассоциации.

Видимость полюса ассоциации ‒ это указание того, является ли классификатор,
присоединенный к данному полюсу ассоциации, видимым для классификаторов (кроме непосредственно
присоединенных), маршруты из которых ведут к нему.

Пожалуйста, не думайте, что мы хотим вас запутать с помощью подобных формальных определений. На самом
деле все очень просто. Продолжим рассмотрение предыдущего примера.

Взгляните на следующий рисунок, отличие которого от предыдущего состоит в том, что добавлена роль на
полюсе ассоциации между классами Company и Department
1. Само по себе наличие роли на полюсе не важно для данного случая.
Главное здесь ‒ присутствие видимости для этого полюса ассоциации, которое имеет значение private и согласно принятой в UML нотации отображается в виде знака
«-» 2.

Пример использования свойства видимости на полюсе ассоциации

Рис. Пример использования свойства видимости на полюсе ассоциации

Теперь интерпретация диаграммы с точки зрения возможных маршрутов изменилась ровно настолько, чтобы
соответствовать требованию технического задания. Имея экземпляр класса Person
можно получить соответствующий экземпляр класса Company, т.е. узнать
компанию, в которой работает данный сотрудник, но нельзя получить список всех отделов, т.е.
экземпляров класса Department, так как навигация по агрегации между
Company и Department запрещена для
всех маршрутов, кроме тех, которые берут своё начало в Company.
Видимость private на полюсе агрегации у класса Department
показывает, что только для непосредственно связанного с ним (данной ассоциацией) классу Company дана возможность осуществить навигацию в этом направлении.

Как видно из рассмотренного примера, видимость на полюсе ассоциации может применяться там, где нужна
более «точная настройка» чем та, которую обеспечивает свойство возможность навигации (см. параграф 3.3.6).

Закончив с этим дополнением, перейдем к следующим.

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

Ограничения первой группы связаны с понятиями упорядоченности объектов на полюсе ассоциации
(ordering) и уникальности (uniqueness), т.е. наличием (отсутствием) в этом множестве
одинаковых объектов.

Если кратность полюса лежит в диапазоне 0..1, то проблемы упорядоченности
не возникает ‒ на данном полюсе связи, возможно, имеется один объект, но не более того. При
иных кратностях объектов может быть несколько, т.е. полюс связи присоединен к множеству объектов. По
умолчанию множество объектов, присоединенных к данному полюсу связи, считается неупорядоченным (как
и любое множество, если не оговорено противное). Соответствующее этому состоянию ограничение {set} можно не указывать, оно является значением по умолчанию. Если
необходимо указать, что это множество упорядочено, то нужно наложить соответствующее ограничение ‒
{ordered} на
полюс ассоциации.

Свойство уникальности связано с ограничением {bag}, которое означает, что
множество объектов, присоединенных к полюсу, допускает включение одного объекта несколько раз. Для
спецификации упорядоченного множества, допускающего повтор объектов, используется ключевое слово
{sequence}.

Данные ограничения можно свести в следующую таблицу.

Табл. Свойства упорядоченности и уникальности

Упорядоченное множество Неупорядоченное множество
Есть одинаковые элементы {sequence}
последовательность
{bag}
мультимножество
Нет одинаковых элементов {ordered}
упорядоченное множество
{set}
множество

Обычно считается, что множество объектов на полюсе связи может изменяться произвольным образом (в
пределах специфицированной кратности). Например, в один момент работы информационной системы отдела
кадров в данном подразделении может быть 10 одних должностей, а в другой ‒ 20 других.
Совершенно аналогично, значение атрибута обычно может произвольным образом меняться в процессе жизни
объекта (в пределах указанного типа атрибута). Однако иногда необходимо определенным образом
ограничить изменяемость атрибута (см. табл. Значения свойства
изменяемости атрибута
в параграф 3.2.2). Аналогично иногда нужно ограничить изменяемость
состава множества объектов присоединенных к полюсу связи (экземпляру ассоциации). Для этого
применяется тот же самый набор стандартных значений (еще раз см. табл. Значения
свойства изменяемости атрибута
).

В информационной системе отдела кадров мы не нашли подходящего примера, и поэтому, для иллюстрации
двух последних понятий рассмотрим элементарный пример из вычислительной геометрии. Допустим, что у
нас есть класс Point, экземплярами которого являются точки (на
плоскости). Многоугольник (класс Polygon) можно определить, как
упорядоченное (ограничение {ordered}) множество точек (вершин
многоугольника), причем резонно предположить, что состав вершин данного многоугольника, после того,
как он определен, не может меняться (ограничение {readOnly}). Модель,
описывающая данную ситуацию, приведена ниже.

Упорядоченность и изменяемость множества объектов на полюсе связи

Рис. Упорядоченность и изменяемость множества объектов на полюсе связи

Вторая группа ограничений появилась только в UML 2 и позволяет манипулировать множествами объектов на
полюсах.

Ограничение {subsets x} полюса ассоциации
(composition) ‒ это указание на то, что множество объектов, соответствующих данному полюсу,
является подмножеством множества объектов полюса x.

Проиллюстрируем данное определение. Для этого рассмотрим следующую задачу: на плоскости заданы N различных точек, (N ≥ 8), принадлежащих
выпуклому восьмиугольнику. При этом известно, что данное множество точек заведомо содержит все
вершины восьмиугольника, а также возможно дополнительные точки, которые не являются вершинами и
лежат на сторонах. Требуется восстановить восьмиугольник и вывести его вершины в порядке обхода по
часовой стрелке, начиная с произвольной вершины. На рисунке приведен пример (N
= 21)
восьмиугольника, при этом закрашенные точки задают вершины, а не закрашенные лежат
на сторонах.

Иллюстрация к задаче о восьмиугольнике

Рис. Иллюстрация к задаче о восьмиугольнике

Алгоритм решения задачи ‒ построение выпуклой оболочки. Возможная структура данных для решения
поставленной задачи описывается следующей диаграммой классов.

Использование ограничения subsets

Рис. Использование ограничения subsets

На приведенной диаграмме экземпляры класса Polygon хранят исходный
массив точек, а экземпляры класса Octagon упорядоченный набор вершин,
получаемый после построения выпуклой оболочки, которым и определяются. Ограничение {subsets p} помещенное на полюсе v 1 указывает, что множество вершин восьмиугольника (то есть,
фактически, множество объектов на полюсе v) является подмножеством
исходных точек (то есть, фактически, подмножество объектов на полюсе p
2).

Ограничение {union} полюса ассоциации (composition) ‒
это указание на то, что множество объектов, соответствующих данному полюсу x,
есть объединение всех подмножеств полюсов с ограничениями {subsets
x}
.

Только что приведенный пример не позволяет объявить полюс p с
ограничением {union}, так как в качестве подмножеств точек p, обозначенные через {subset p} явно
выделены только вершины многоугольника. Неучтенными остались точки лежащие на сторонах. Исправим эту
ситуация и будем отдельно хранить эти точки (класс SidePoint 1). Диаграмма классов в соответствии с этим изменением примет вид,
представленный ниже.

Использование ограничения union

Рис. Использование ограничения union

Теперь мы вправе написать {union} рядом с полюсом p 2. Напомним, что ограничение {union} в данном случае означает, что каждая из точек множества p (множества всех точек), либо является вершиной восьмиугольника 3, либо лежит на его стороне 4.

В процессе проектирования возможны ситуации, когда ассоциация должна иметь собственные атрибуты (и
даже операции), значения которых хранятся в экземплярах ассоциации ‒ связях. В таком случае
применяется специальный элемент моделирования ‒ класс ассоциации.

Класс ассоциации (association class) ‒ это сущность, которая является
ассоциацией, но также имеет в своем составе составляющие класса.

Класс ассоциации изображается в виде символа класса, присоединенного пунктирной линией к линии
ассоциации.

Вернемся к информационной системе отдела кадров.

ИЗМЕНЕНИЯ В ТЕХНИЧЕСКОМ ЗАДАНИИ

Допускается ситуация, когда сотрудник может работать на нескольких должностях в разных проектах, а также возможно, чтобы одну и ту же должность в одном проекте занимало несколько сотрудников (дробление ставки). Размер заработной платы зависит от того, сколько конкретно времени проработал данный сотрудник в данной должности в данном проекте.

Таким образом, имеет место более сложное отношение между должностями, сотрудниками и проектами,
нежели те, что приведены выше. А именно, допускается не только совмещение должностей (один сотрудник
может работать на нескольких должностях в разных проектах), но и дробление ставок (одну должность
могут занимать несколько сотрудников ‒ полставки, четверть ставки и т.п.). Используя же
разобранную нотацию ассоциации, мы можем констатировать, что между классами Person,
Position и Project имеет место
ассоциация «многие ко многим». Однако этого недостаточно: необходимо указать, какую долю данной
должности занимает данный сотрудник. Эту информацию нельзя отнести ни к должности, ни к сотруднику,
ни к проекту ‒ это атрибут ассоциации, которая всех их связывает. На следующем рисунке показан
способ использования класса ассоциации 1 для решения данной задачи.

Класс ассоциации

Рис. Класс ассоциации

Может показаться, что понятие класса ассоциации в UML является надуманным и излишним. Действительно,
можно применить стандартный прием нормализации, который часто используется при проектировании схем
баз данных: систематическим образом избавиться от отношений «многие ко многим» путем введения
дополнительной сущности 1 и трех отношений «один ко многим».
Применительно к данному примеру такой прием дает решение, приведенное на следующем рисунке.

Элиминация отношения 'многие ко многим' с помощью введения дополнительной сущности

Рис. Элиминация отношения «многие ко многим» с помощью введения дополнительной
сущности

На первый взгляд, модели на двух предыдущих рисунках выглядят семантически эквивалентными, однако это
не так ‒ здесь есть тонкое различие. В случае использования класса ассоциации Job 1 на рис. Класс ассоциации,
который по определению является множеством троек должность–сотрудник–проект, не может иметь двух
одинаковых троек. То есть не может быть так, чтобы Иванов занимал полставки архитектора в одном
проекте и еще (отдельно) четверть той же ставки в этом же проекте. В случае же промежуточного класса
Job 1 на рис. Элиминация отношения
«многие ко многим» с помощью введения дополнительной сущности
это, вообще говоря, вполне
возможно, что не совсем естественно. Опытные проектировщики баз данных нам могут возразить, что это
вполне поправимо: нужно ввести в классах Position, Person и Project атрибуты, которые
будут уникальными ключами, идентифицирующими объекты этих классов, а в классе Job ввести тройку атрибутов, значениями которых будут ключи классов
Position, Person и Project и потребовать, чтобы эта тройка атрибутов была уникальным
составным ключом класса Job. Мы не будем спорить ‒ это
действительно так и делается при традиционном проектировании схем баз данным. Нам представляется,
что разбор данного примера уже является достаточным обоснованием полезности элегантного понятия
класса ассоциации в UML: один рисунок с классом-ассоциацией понятнее и точнее фрагмента
нормализованной схемы данных с неизбежными текстовыми добавлениями и пояснениями про уникальные
ключи.

Продолжим разговор про специфику отношений между классами Company и
Person. Мы видим, что они связаны отношением ассоциации с кратностями
полюсов «один ко многим». Такая ассоциация доставляет для каждого экземпляра класса Company, находящегося на полюсе с кратностью «один» множество
(иногда большое) объектов класса Person, находящегося на полюсе с
кратностью «много». Однако часто возникают ситуации, когда нужно получить не все множество
ассоциированных объектов Person, а некоторое небольшое подмножество,
чаще всего один конкретный объект. Чтобы выделить из множества один конкретный объект, нужно
располагать информацией, однозначно идентифицирующей этот объект. Такую информацию принято называть
ключом. Например, индивидуальный номер налогоплательщика (ИНН) для сотрудника в информационной
системе отдела кадров может считаться ключом.

Для того чтобы решить задачу поиска конкретного сотрудника (конкретный экземпляр класса Person) всегда можно применить следующее тривиальное решение: хранить
ключ (ИНН) в самом объекте класса Person в качестве атрибута и,
получив множество объектов, перебирать их все последовательно до тех пор, пока не найдется тот,
который имеет искомое значение ключа. Такой прием называется линейным поиском. Для компаний, в
которых не очень много сотрудников, данный способ может быть вполне приемлемым. Но в других случаях,
когда к полюсу с кратностью «много» присоединено действительно много объектов, линейный поиск
слишком неэффективен. Известно множество структур данных, позволяющих эффективно выделить (найти в
множестве) объект по ключу: сортированные массивы, таблицы расстановки (хэш-таблицы), деревья
сортировки, внешние индексы и др. Эти приемы обобщены в UML понятием квалификатора.

Квалификатор полюса ассоциации (qualifier) ‒ это атрибут (или несколько
атрибутов) полюса ассоциации, значение которого (которых) позволяет выделить один (или
несколько) объектов класса, присоединенного к другому полюсу ассоциации.

Квалификатор изображается в виде небольшого прямоугольника на полюсе ассоциации, примыкающего к
прямоугольнику класса. Внутри этого прямоугольника (или рядом с ним) указываются имена и, возможно,
типы атрибутов квалификатора. Описание квалифицирующего атрибута ассоциации имеет такой же
синтаксис, что и описание обычного атрибута класса, только оно не может содержать начального
значения.

Основное назначение квалификатора ‒ снизить кратность противоположного полюса ассоциации,
поэтому в основном он используется в ассоциациях с кратностями полюсов «один ко многим» или «многие
ко многим» и стоит у полюса противоположному полюсу с кратностью «много».

При использовании квалификатора кратность противоположного полюса снижается, и это отображается на
диаграмме.

Квалификатор

Рис. Квалификатор

Кратность полюса у класса Person изменилась с * до 0..1, так как экземпляр класса Person для данного ключа может быть найден, а может и отсутствовать
(неправильное значение ключа).

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

Мы хотим подвести итог этому пространному разделу, обобщив сказанное с помощью диаграммы метамодели
ассоциации, представленной на следующей диаграмме.

Метамодель ассоциации

Рис. Метамодель ассоциации

Нам хочется закончить обсуждение диаграмм классов ‒ важнейшего средства описания структуры в UML ‒
серией элементарных советов по практическому моделированию структуры. Как видно из предыдущих
разделов, диаграммы классов содержат множество деталей. Для практически значимых систем диаграммы
классов в конечном итоге получаются довольно сложными. Пытаться прорисовать сложную диаграмму
классов сразу «на всю глубину» нерационально ‒ слишком велик риск «утонуть» в деталях. Мы
полагаем, что удачная модель структуры сложной системы создается за несколько (может быть, даже за
несколько десятков) итераций, в которых моделирование структуры перемежается моделированием
поведения (см. главу 4). Вот наши советы.

  • Описывать структуру удобнее параллельно с описанием поведения. Каждая итерация должна быть
    небольшим уточнением, как структуры, так и поведения.
  • Не обязательно включать в модель все классы сразу. На первых итерациях достаточно
    идентифицировать очень небольшую (10%) долю всех классов системы.
  • Не обязательно определять все составляющие класса сразу. Начните с имени класса ‒ операции
    и атрибуты постепенно выявятся в процессе моделирования поведения.
  • Не обязательно показывать на диаграмме все составляющие класса и их свойства. В процессе работы
    диаграмма должна легко охватываться одним взглядом.
  • Не обязательно определять все отношения между классами сразу. Пусть класс на диаграмме «висит в
    воздухе» ‒ ничего с ним не случится.

Мы действительно сами следуем своим советам. Например, приведенные выше диаграммы это, как нетрудно
видеть, промежуточные шаги при моделировании структуры предметной области нашей системы.
Окончательный результат со всеми важными подробностями приведен ниже. Диаграмма достаточно хорошо
читается ‒ в ней не слишком много деталей. Для уточнения можно использовать дополнительные
диаграммы, иллюстрирующие определенные аспекты модели.

Основная диаграмма классов ИС ОК

Рис. Основная диаграмма классов ИС ОК

Шесть типов классовых отношений

Существует шесть основных типов отношений между классами: наследование, реализация/реализация, композиция, агрегация, ассоциация и зависимость. Стрелки для шести отношений следующие:

Тогда мы приходим к пониманию конкретного содержания классовых отношений.

UML class diagram relationships

Шесть типов отношений

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

Наследование

Наследование также называется обобщением и используется для описания отношений между родительским и дочерним классами. Родительский класс также называется базовым классом, а подкласс также называется производным классом.

В отношениях наследования подкласс наследует все функции родительского класса, а родительский класс имеет все атрибуты, методы и подклассы. Подклассы содержат дополнительную информацию в дополнение к той же информации, что и родительский класс.

Например: автобусы, такси и автомобили — это автомобили, у них у всех есть имена, и все они могут находиться в дороге.

Реализация / Внедрение

Реализация (Implementation) в основном используется для указания связи между интерфейсами и классами реализации .

Интерфейс (включая абстрактный класс ) — это набор методов. В отношениях реализации класс реализует интерфейс, а методы в классе реализуют все методы объявления интерфейса.

Например: автомобили и корабли — это транспортные средства, а транспортное средство — всего лишь абстрактное понятие мобильного средства, а корабль и транспортное средство реализуют конкретные мобильные функции.

Связь композиции

Композиция: Отношения между целым и частью, но целое и часть не могут быть разделены .

Комбинированное отношение представляет собой отношение между целым и частью класса, а общее и часть имеют согласованное время жизни. Как только объект в целом перестанет существовать, некоторые объекты не будут существовать, и все они умрут в одной и той же жизни. Например, человек состоит из головы и тела. Они неразделимы и сосуществуют.

Отношения агрегации

Агрегация: отношения между целым и частью, а также целым и частью могут быть разделены.

Агрегатные отношения также представляют отношения между целым и частью класса, объекты-члены являются частью общего объекта, но объект-член может существовать независимо от общего объекта.

Например, водители автобусов, рабочая одежда и головные уборы являются частью общих отношений, но их можно разделить. Рабочую одежду и головные уборы можно надевать на других водителей. Водители автобусов также могут носить другую рабочую одежду и головные уборы.

Отношения ассоциации

Ассоциация: указывает, что свойство класса содержит ссылку на экземпляр (или экземпляры) другого класса .

Ассоциация — это наиболее часто используемая связь между классом и классом, что означает наличие связи между одним типом объекта и другим типом объекта. Комбинации и агрегации также относятся к ассоциативным отношениям , но отношения между классами принадлежности слабее двух других.

Существует четыре вида ассоциаций : двусторонние ассоциации , односторонние ассоциации , самоассоциация и многозначные ассоциации .

Например: машины и водители, одна машина соответствует конкретному водителю, и один водитель может управлять несколькими машинами.

На диаграммах UML двунаправленные ассоциации могут иметь две стрелки или не иметь стрелок , а односторонние ассоциации или самоассоциации имеют стрелку .

В отношении множественности вы можете добавить число непосредственно к связанной строке, чтобы указать количество объектов в соответствующем классе.

  • 0..1: Нет или только один

  • m..n: не менее m, не более n (m<=n)

Зависимости

Зависимость: предположим, что изменение в классе А вызывает изменение в классе В, тогда скажем, что класс В зависит от класса А.

В большинстве случаев зависимости отражаются в методах класса, использующих в качестве параметра объект другого класса .

Отношение зависимости — это отношение «использования». Изменение в конкретной вещи может повлиять на другие вещи, которые ее используют, и использовать зависимость, когда необходимо указать, что одна вещь использует другую. Например: Автомобиль работает на бензине. Если нет бензина, машина не сможет ехать.

Диаграмма классов — система заказов

Диаграмма классов ниже моделирует заказ клиента из розничного каталога. Центральным классом является Орден . С ним связаны Клиент , совершающий покупку, и Платеж . Оплата может быть одного из четырех видов: наличные , чек , кредит или банковский перевод . Заказ содержит OrderDetails (элементы строки), каждый со своим связанным Item .

Диаграмма классов с пользовательским ограничением

類圖模板:類圖 - 類和包約束(由 Visual Paradigm Online 的類圖製作者創建)

ИЗМЕНИТЬ ЭТОТ ШАБЛОН

Среди шести типов отношений кодовая структура комбинации, агрегации и ассоциации одинакова, и ее можно понять по силе отношения. Порядок от сильного к слабому: наследование → реализация → композиция → агрегация → ассоциация → зависимость . Ниже приведена полная диаграмма UML.

использованная литература

UML означает Унифицированный язык моделирования. Это богатый язык для моделирования программных решений, структур приложений, поведения систем и бизнес-процессов. Существует 14 типов диаграмм UML, которые помогут вам смоделировать это поведение. Вы можете , используя наше программное обеспечение, или посмотреть примеры UML-диаграмм в нашем сообществе диаграмщиков.

Список типов диаграмм UML

Итак, каковы же различные типы диаграмм UML? Существуют две основные категории: структурные диаграммы и поведенческие диаграммы. Щелкните по ссылкам, чтобы узнать больше о конкретном типе диаграммы.

  • Структурные диаграммы
    • Диаграмма классов
    • Диаграмма компонентов
    • Диаграмма развертывания
    • Диаграмма объектов
    • Диаграмма пакета
    • Диаграмма профиля
    • Диаграмма композитной структуры
  • Поведенческие диаграммы
    • Диаграмма вариантов использования
    • Диаграмма деятельности
    • Диаграмма машины состояний
    • Диаграмма последовательности
    • Диаграмма связи
    • Диаграмма обзора взаимодействия
    • Временная диаграмма

Все 14 типов диаграмм UML делятся на поведенческие и структурные UML

Структурные диаграммы показывают вещи в моделируемой системе. В более техническом смысле они показывают различные объекты в системе. Поведенческие диаграммы показывают, что должно происходить в системе. Они описывают, как объекты взаимодействуют друг с другом, создавая функционирующую систему.

Диаграмма классов

Диаграммы классов являются основным строительным блоком любого объектно-ориентированного решения. Он показывает классы в системе, атрибуты и операции каждого класса, а также отношения между каждым классом.

В большинстве инструментов моделирования класс состоит из трех частей. Имя вверху, атрибуты в середине и операции или методы внизу. В большой системе с большим количеством связанных классов классы группируются вместе для создания диаграмм классов. Различные отношения между классами показаны разными типами стрелок.

Ниже приведено изображение диаграммы классов. Перейдите по ссылке ниже для просмотра примеров диаграмм классов или начните работу с нашими Шаблоны диаграмм.

Диаграмма классов, самый популярный тип диаграмм UML

Нажмите на изображение для редактирования диаграммы классов (откроется в новом окне)

Получить больше примеров диаграмм классов UML >>

Диаграмма компонентов

Диаграмма компонентов отображает структурную взаимосвязь компонентов программной системы. В основном они используются при работе со сложными системами с большим количеством компонентов. Компоненты взаимодействуют друг с другом с помощью . Интерфейсы связаны между собой с помощью коннекторов. На изображении ниже показана диаграмма компонентов.

Шаблон диаграммы компонентов с пояснениями

Вы можете использовать этот шаблон диаграммы компонентов, нажав на изображение

Получить больше шаблонов диаграмм компонентов >>

Диаграмма развертывания

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

Шаблон диаграммы развертывания

Нажмите на изображение, чтобы использовать эту диаграмму развертывания в качестве шаблона

Получить больше шаблонов диаграмм развертывания >>

Диаграмма объектов

Диаграммы объектов, иногда называемые диаграммами экземпляров, очень похожи на диаграммы классов. Как и диаграммы классов, они также показывают отношения между объектами, но в них используются реальные примеры. Они показывают, как будет выглядеть система в определенный момент времени. Поскольку в объектах имеются данные, они используются для объяснения сложных отношений между объектами.

Шаблон диаграммы объектов

Нажмите на изображение, чтобы использовать диаграмму объектов в качестве шаблона

Получить больше шаблонов диаграмм объектов >>

Диаграмма пакета

Как следует из названия, диаграмма пакетов показывает зависимости между различными пакетами в системе. Ознакомьтесь со статьей wiki, чтобы узнать больше о зависимостях и элементах, встречающихся в диаграммах пакетов Пример диаграммы пакетов UML

Диаграмма профиля

Диаграмма профиля – это новый тип диаграмм, введенный в UML 2. Это тип диаграммы, который очень редко используется в каких-либо спецификациях. Для получения дополнительных шаблонов диаграмм профилей посетите наше сообщество диаграммДиаграмма профиля UML

Диаграмма композитной структуры

Диаграммы составной структуры используются для отображения внутренней структуры класса. Некоторые из распространенных грамм Диаграмма составной структуры UML, построенная с помощью Creately

Диаграмма вариантов использования

Как наиболее известный тип диаграмм среди поведенческих типов UML, диаграммы Use case дают графический обзор участников системы, различных функций, необходимых этим участникам, и того, как эти различные функции взаимодействуют. Это отличная отправная точка для обсуждения любого проекта, поскольку вы можете легко определить основных действующих лиц и основные процессы системы. Вы можете создать диаграммы вариантов использования с помощью нашего инструмента и/или сразу же приступить к работе, используя наши шаблоны вариантов использования. Взаимосвязи диаграммы Use Case объясняются на примерах

Рисование диаграммы вариантов использования с помощью Creately

Нажмите на изображение для редактирования этого шаблона

Получить больше примеров диаграмм вариантов использования >>

Диаграмма деятельности

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

Диаграмма деятельности, построенная с помощью Creately

Получить больше шаблонов диаграмм деятельности >>

Диаграмма машины состояний

Диаграммы машин состояний похожи на диаграммы деятельности, хотя обозначения и использование немного меняются. Иногда их также называют диаграммами состояний или диаграммами диаграмм состояний. Они очень полезны для описания поведения объектов, которые ведут себя по-разному в зависимости от того, в каком состоянии они находятся в данный момент. Приведенная ниже диаграмма государственной машины показывает основные состояния и действия.

Получить больше примеров диаграмм состояния >>

Диаграмма последовательности

Диаграммы последовательностей в UML показывают, как объекты взаимодействуют друг с другом и в каком порядке происходит это взаимодействие. Важно отметить, что они показывают взаимодействие для конкретного сценария. Процессы представлены вертикально, а взаимодействия показаны в виде стрелок. В этой статье рассказывается об чении, а также об основах диаграмм последовательности. Также ознакомьтесь с полным учебником по диаграммам последовательности, чтобы узнать больше о диаграммах последовательности. Вы также можете мгновенно начать рисовать, используя наши .

Диаграмма последовательности, нарисованная с помощью Creately

Диаграмма последовательности, нарисованная с помощью Creately

Диаграмма связи

В UML 1 они назывались диаграммами сотрудничества. Диаграммы связи похожи на диаграммы последовательности, но основное внимание уделяется сообщениям, передаваемым между объектами. Одна и та же информация может быть представлена с помощью диаграммы последовательности и различных объектов. здесь, чтобы понять разницу на примере Диаграмма связи, нарисованная с помощью Creately

Диаграмма обзора взаимодействия

Диаграммы обзора взаимодействия очень похожи на диаграммы деятельности. В то время как диаграммы деятельности показывают последовательность процессов, диаграммы обзора взаимодействия показывают последовательность диаграмм взаимодействия. Они представляют собой набор диаграмм взаимодействия и порядок их возникновения. Как уже упоминалось, существует семь типов диаграмм взаимодействия, поэтому любой из них может быть узлом в обзорной диаграмме взаимодействияОбзорная диаграмма взаимодействия UML, нарисованная с помощью Creately

Временная диаграмма

Временные диаграммы очень похожи на диаграммы последовательности. Они представляют собой поведение объектов в определенный промежуток времени. Если речь идет только об одном объекте, то диаграмма проста. Но если задействовано более одного объекта, используется диаграмма Timing, чтобы показать взаимодействие между объектами в течение данного временного интервала. Нажмите здесь, чтобы создать свою схему синхронизации. Временная диаграмма UML, нарисованная с помощью Creately Выше перечислены все типы диаграмм UML. UML предлагает множество типов диаграмм, и иногда две диаграммы могут объяснять одно и то же, используя разные обозначения. Ознакомьтесь с этой статьей блога, чтобы узнать, UML-диаграмма вам больше всего подходит. Если у вас есть вопросы или предложения, не стесняйтесь оставлять комментарии.

Введение и содержание

Диаграмма классов занимает центральное место в проектировании объектно-ориентированной системы. Нотация классов используется на разных этапах проектирования и строится с различной степенью детализации. Язык UML применяется не только для проектирования, но и с целью документирования, а также эскизирования проекта. Я (в отличии от Гради Буча) не являюсь сторонником разработки проекта с использованием всех видов UML диаграмм, а также детального проектирования. Чаще всего я применяю UML для эскизирования, а также для проектирования по процессу ICONIX [Rosenberg]. В статье описана часть нотации классов UML, применение которой достаточно в большинстве случаев. Тут не будет информации о кратности ассоциаций и атрибутов, особенностях изображения параллельных операций, шаблонах (параметризованных классах) и ограничениях. При необходимости всю эту информации можно посмотреть в других книгах [Buch, Leonenkov]. Мы же ограничимся базовой частью нотации и больше внимания уделим применению диаграммы классов.

  1. Элементы диаграммы классов
    1. Символ класса
    2. Отношения классов
  2. Использование диаграммы классов
    1. Диаграмма классов как словарь системы, концептуальная модель
    2. Диаграмма классов уровня проектирования
    3. Диаграмма классов для эскизирования, документирования
    4. Диаграмма классов для моделирования БД
  3. Заключение и литература по теме

1 Элементы диаграммы классов

На диаграмме классов с помощью специальных символов изображаются типы данных программы и отношения между ними, хотя в некоторых случаях могут использоваться и некоторые другие элементы — пакеты и даже экземпляры классов (объекты) [Leonenkov].

1.1 Символ класса

Символ класса на диаграмме может выглядеть различным образом в зависимости от детализации диаграммы:

Вопросы детализации будут рассмотрены в следующих разделах, а сейчас надо обратить внимание, что символ класса содержит имя (Player), набор операций (move, get_gealth) и атрибутов (pos, state). Для элементов класса могут задаваться тип, кратность, видимость и т.д.:

Формат спецификации атрибута:
видимость имя : тип [кратность] = значение_по_умолчанию

Формат спецификации операции:
видимость имя(аргумент: тип) = тип_возвращаемого_значения

В зависимости от параметра видимости элемент может быть:

  • приватным (private, доступен только внутри класса) — задается символом «минус» (-), может отображаться в виде квадрата;
  • защищенным (protected, доступен внутри класса, а также внутри классов-наследников) — задается символом «решетка» (#), может отображаться в виде ромба;
  • открытым (public, доступен всем) — задается символом «плюс» (+), может отображаться в виде круга.

Виртуальная функция и имя абстрактного класса выделяются курсивом, а статическая функция — подчеркивается.

1.2 Отношения классов

Диаграмма классов допускает различные виды отношений, рассмотрим их на части диаграммы модели некоторой игры:

В игре есть различные виды элементов (стены, сундуки, персонажи). Все эти элементы являются наследниками абстрактного класса AbstractItem, при этом часть из них умеет двигаться (такие элементы должны быть унаследованы от MovingItem). Наследование (отношение «является») изображается с помощью сплошной линии с закрытой стрелки, направленной в сторону суперкласса — на диаграмме класс MovingItem унаследован от AbstractItem, класс Player — от MovingItem и т.д. Штриховая линия с закрытой стрелкой задает отношение реализации (закрытое наследование).

Другой вид отношений между классами — включение, в объектно-ориентированном программировании различают два вида этого отношения — композицию и агрегацию. Напомню, что композиция — это разновидность включения, когда объекты неразрывно связаны друг с другом (время их жизни совпадает), в случае агрегации, время жизни различно (например, когда объект вложенного класса может быть заменен другим объектом во время выполнения программы).

Отношение композиции обозначается закрашенным ромбом, который рисуется со стороны включающего класса — так, класс MovingItem включает в себя класс Position, т.к. перемещающийся объект всегда имеет позицию. Отношение агрегации изображается незакрашенным ромбом — игрок (Player) агрегирует состояние (IPlayerState).

Если вы знакомы с паттернами State, Strategy или Delegation — секцию можно пропустить.
На приведенной выше диаграмме используется шаблон проектирования Состояние (State), являющийся разновидностью шаблона Делегирование (Delegation) и близкой к паттерну Стратегия (Strategy). Суть делегирования заключается в том, что для упрощения логики работы класса, часть его работы может быть передана (делегирована) вспомогательному классу. В свою очередь, паттерн State может быть добавлен, например, на этапе рефакторинга если в нескольких функциях класса встречается разлапистая проверка состояния объекта для выполнения тех или иных действий. В нашем случае персонаж может взаимодействовать с ежом, предположим, что если персонаж движется сидя и контактирует с ежом — у него должно уменьшится здоровье, а если стоя — увеличится счет (points). Кроме ежа могла быть еда, противники, патроны и т.д. Для демонстрации такого паттерна создан абстрактный класс IPlayerState и два наследника StayState и SeatState. В классе Player, при нажатии кнопки Ctrl состояние могло бы меняться на SeatState, а при отпускании — на StayState. Таким образом, при выполнении state->process_hedgehog(this) наш игрок каким-то образом, определенным объектом state, проконтактирует с ежиком.

Шаблон проектирования Delegation (и все его разновидности) — хороший пример для демонстрации агрегации. В нашем случае состояние игрока может меняться за счет изменения объекта по указателю, т.е. время жизни объектов различается.

Наиболее общий вид отношений между классами — ассоциация, обозначается сплошной линией (иногда со стрелкой). Вообще, и композиция, и агрегация, и обобщение (наследование) — являются частными случаями ассоциации. В нашей диаграмме с помощью ассоциации показано, что класс IPlayerState изменяет stats (health и points) объекта Player. Ассоциация может иметь название связи, поясняющую суть отношения. В качестве названия связей композиции и агрегации часто используется имя соответствующей переменной. Кроме того, ассоциация может иметь кратность, она задается на концах линии:

  • 1 — одна связь (на нашей диаграмме показано, что один игрок включает в себя один экземпляр класса IPlayerState);
  • * любое число связей (если бы на диаграмме был класс игрового поля, то с помощью звездочки можно было бы показать, что оно может содержать произвольное число игровых элементов);
  • [от..до] — может задаваться диапазоном. Так диапазон [0..*] эквивалентен звездочке, но если мы захотим показать, что должно присутствовать более одного объекта — можем записать [1..*]

Последний вид отношений, который мы рассмотрим — зависимость, изображается штриховой (прерывистой) линией. Если есть стрелка — то направлена от зависимого к независимому классу, если стрелки нет — то классы зависят друг от друга. Под зависимостью понимается зависимость от интерфейса, т.е. если интерфейс независимого класса изменится — то придется вносить изменения в зависимый класс. В нашей диаграмме SeatState и StayState зависят от класса Player, т.к. обращаются к его методам для изменения характеристик игрока. Для изображения отношения дружбы между классами используется отношение зависимости с подписью friend.

Очевидно, что не все виды отношений стоит отображать на диаграмме и одни отношения могут быть заменены другими. Так, я убрал бы из нашего примера отношения зависимости, однако при некоторых обстоятельствах (например при эскизировании на маркерной доске) они были бы вполне уместны. Расстановка кратности и имен связей тоже выполняется далеко не во всех случаях. Вообще, не стоит помещать на диаграмму лишнюю информацию. Главное — диаграмма должна быть наглядной.

2 Использование диаграммы классов

Мы рассмотрели основные обозначения, используемые на диаграммах классов — их должно быть достаточно в подавляющем большинстве случаев. По крайней мере, владея этим материалом вы легко сможете разобраться в диаграммах шаблонов проектирования и понять эскиз любого проекта. Однако, как правильно строить такие диаграммы? В каком порядке и с какой степенью детализации? — ответ зависит от целей построения диаграммы, поэтому приведенный материал будет разбит на подразделы в соответствии с целями моделирования.

Стоит отметить, что у Гради Буча советы по использованию UML даны в книге «Руководство пользователя» [Buch_Rambo], но в его «Объектно-ориементированном анализе» [Buch] можно найти хорошие примеры и критерии качества проекта. Леоненков [Leonenkov] и вовсе избегает этой темы, оставляя лишь ссылки на литературу, конкретные рекомендации я нашел у Лармана [Larman] и Розенберга [Rosenberg], часть материала основана на моем личном опыте. Фаулер рассматривает UML как средство эскизирования, поэтому у него свой (сильно отличающийся от Буча и Розенберга) взгляд на диаграмму классов [Fauler].

2.1 Диаграмма классов как словарь системы, концептуальная модель

Словарь системы формируется параллельно с разработкой диаграммы прецедентов, т.е. технического задания. Выглядит это следующим образом — вы задаете заказчику вопросы типа «что еще может сделать пользователь?», «что произойдет (должна выдать система) если пользователь сделает нажмет на <эту> кнопку?», а ответы на них записываете в виде описания прецедентов. Однако, заказчик, давая ответы может называть одни и те же вещи разными именами — из личного опыта: говоря «клетка», «пересечение», «узел» и «ячейка» заказчик может иметь ввиду одно и тоже. В вашей же системе все эти понятия должны быть представлены одной абстракцией (классом/функцией/…). Для этого при общении с заказчиком стоит фиксировать терминологию в виде словаря системы — очень хорошо с этим справляется диаграмма классов.

Гради Буч для построения словаря системы предлагает выполнять в следующем порядке [BuchRambo]:

  1. анализируя прецеденты, определить какие элементы пользователи и разработчики применяют для описания задачи или ее решения;
  2. выявить для каждой абстракции соответствующее ей множество обязанностей (ответственности). Проследите правильность распределения обязанностей (в том числе, соблюдение принципа единой обязанности [solid_refactoring]);
  3. разработайте процедуры и операции для выполнения классами своих обязанностей.

В качестве примера рассмотрим словарь системы для игры «Сапер». На приведенной ниже диаграмме показан вариант, который получился в результате обсуждения задачи у моего студента. Видно, что на диаграмме изображены сущности и их атрибуты, понятные для заказчика, эту диаграмму стоит иметь перед глазами при составлении прецедентов чтобы не называть «Клетку» — «Полем», вводя всех в заблуждение. При построении словаря системы следует избегать нанесения на диаграмму функций классов, т.к. настолько детализированное распределение обязанностей лучше выполнять после построения диаграмм взаимодействия.

В процессе проектирования словарь системы может дополняться, Розенберг очень хорошо демонстрирует это в своей книге описывая итеративный процесс проектирования ICONIX [Rosenberg]. Например, после рассмотрения нескольких прецедентов может оказаться, что несколько классов реализуют один и тот же функционал — для решения проблемы надо более четко прописать обязанности каждого класса, возможно, добавить новый класс и перенести часть этих обязанностей ему.

Ларман предлагает строить концептуальную модель системы [Larman] — это примерно то, что мы описали как словарь системы, но помимо терминов предметной области в ней фиксируются некоторые отношения, понятные заказчику. Например, заказчик понимает (и фиксирует в техническом задании), что <покупку> оформляет <продавец> — следовательно, между продавцом и покупкой существует отношение ассоциации "оформляет". Я рекомендую строить концептуальную модель, дорабатывая словарь системы, хотя Ларман рекомендует сначала добавлять ассоциации, а затем — атрибуты.

2.2 Диаграмма классов уровня проектирования

В любом объектно-ориентированном процессе проектирования диаграмма классов является результатом, т.к. является моделью, наиболее близкой к реализации (коду). Существуют инструменты, способные преобразовать диаграмму классов в код — такой процесс называется кодогенерацией и поддерживается множеством IDE и средств проектирования. Например, кодогенерацию выполняет Visual Paradigm (доступно в виде плагинов для множества IDE), новые версии Microsoft Visual Studio, такие средств UML-моделирования как StarUML, ArgoUML и др. Чтобы построить по диаграмме хороший код, она должна быть достаточно подробной. Именно о такой диаграмме идет речь в этом разделе.

До Ларману [Larman] до начала построения диаграммы классов уровня проектирования должны быть построены диаграммы взаимодействия и концептуальная модель системы. При этом порядок построения диаграммы следующий:

  1. перенести классы с диаграммы последовательности;
  2. добавить атрибуты концептуальной модели;
  3. добавить имена методов по анализу диаграмм взаимодействия (например, диаграмм последовательностей [uml_sequence_diag]);
  4. добавить типы атрибутов и методов;
  5. добавить ассоциации (на основании атрибутов — отношения композиции и агрегации);
  6. добавить стрелки (направление ассоциаций)
  7. добавить ассоциации, определяющие другие виды отношений (в первую очередь, наследование).

Отношения, добавляемые на диаграмму классов уровня проектирования отличаются от тех, что были в концептуальной модели тем, что они могут быть не очевидны для заказчика (эту диаграмму он вообще смотреть не должен — она разрабатывается для программистов). Если на этапе анализа технического задания мы могли выделить основные сущности, не задумываясь о том, как это будет реализовано, то теперь обязанности между нашими классами должны быть окончательно распределены.

Например, при анализе задания на игру «Сапер» мы выделили классы <Флажок> и <Мина>, но будут ли эти классы в окончательном проекте или останутся только в воображении? — решение можно принять только проанализировав диаграммы взаимодействия. Ведь возможен и такой код:

enum class CellType { 
  EmptyOpened, EmptyClose, EmptyCloseFlagged, 
  MineOpened, MineClose,  MineCloseFlagged 
};

class PlayingGround {
// ...
  CellType **m_ground;
}

Поясню (для тех, кто не пишет на С++) — тут создается перечисление, которое задает тип ячейки. Ячейка может принимать одно из этих шести значений (пустая открытая, пустая закрытая, пустая закрытая с флажком и т.п.). В таком случае, ячейка никак не сможет сама реагировать на нажатия мыши и отвечать за свое отображение (например пустая открытая должна выводить число мин вокруг себя) — все эти обязанности, видимо, лягут на класс PlayingGround.

Пример выше утрированный и однозначно не является образцом хорошего проектирования — на класс PlayingGround возложено слишком много обязанностей, но могли ли мы учесть это при анализе технического задания? Сможем ли мы это сделать до разработки диаграмм взаимодействия для проекта любой сложности? — именно поэтому построение диаграммы классов является последним этапом проектирования.

2.3 Диаграмма классов для эскизирования, документирования

Под эскизированием понимают моделирование некоторой (интересной нам в данный момент) части системы. Например, эскизирование может выполняться на маркерной доске когда в вашу компанию попадет новый сотрудник и вы будете помогать ему «влиться» в существующий проект. Очевидно, что если если дать человеку диаграмму классов уровня проектирования — разбираться он будет долго. Суть эскизирования в избирательности — вы выносите на диаграмму только те элементы, которые важны для пояснения того или иного механизма.

Сторонником применения UML для эскизирования является Фаулер [Fauler], который считает, что целостный процесс проектирования с использованием UML слишком сложен. Эскизирование применяется очень часто (не только при объяснении проекта на маркерной доске):

  • в любой книге, посвященной паттернам проектирования, вы найдете массу UML диаграмм, выполненных в этом стиле;
  • при моделировании прецедента выбираются классы, за счет которых этот прецедент реализуется. Моделирование прецедента выполняется при рефакторинге;
  • в документацию для разработчиков нет смысла вставлять диаграмму классов уровня проектирования — гораздо полезнее описать наиболее важные (ключевые) моменты системы. Для этого строятся эскизные диаграммы классов и диаграммы взаимодействия. Также существуют специальные инструменты построения документацию по готовому коду — такие как JavaDoc или Doxygen [doxygen_codegeneration], в частности они строят диаграмму классов, но чтобы документация была понятной, в исходный код программы требуется вносить комментарии специального вида.

Каких-либо конкретных рекомендаций к эскизам диаграмм классов предложить невозможно, кроме того, обычно это достаточно простая задача. Важно понимать суть — избирательность представления элементов снижает сложность восприятия диаграммы.

2.4 Диаграмма классов для моделирования БД

Частным случаем диаграммы классов является диаграмма «сущность-связь» (E-R диаграмма), используемая для моделирования логической схемы базы данных. В отличии от классических E-R диаграмм, диаграмма классов позволяет моделировать поведение (триггеры и хранимые процедуры).

Обычно ситуация выглядит следующим образом — вы разработали систему, состояние которой нужно сохранять между запусками, например:

  • в вашей игре надо хранить информацию о достижениях пользователя — пройденные уровни, набранные очки и т.п.;
  • если игра сетевая — то может существовать сервер, на котором хранятся достижения разных игроков;
  • ваше приложение для телефона записывает координаты пользователя и позволяет ему оставлять пометки на карте. Вся эта информация тоже не должна уничтожаться после закрытия приложения.

Хранимые между запусками данные должны каким-то образом загружаться по запросу пользователя, т.е. должны задаваться параметры соответствующих классов. Например, приложение должно получить из базы данных список треков (маршрутов) и отобразить его в виде списка в меню программы. При выборе элемента списка — запросить в БД параметры трека, создать объект трека и отобразить его на карте. В любом случае, данные с базы используются при инициализации объектов программы — это важно понимать.

Для моделирования схемы БД с помощью диаграммы классов нужно [Buch_Rambo]:

  1. идентифицировать классы, данные которых должны храниться между запусками приложения (или обращениями пользователя) и нанести эти классы на отдельную диаграмму;
  2. детально специфицировать атрибуты классов, ассоциации и кратности. В E-R модели кратности имеют огромное значение — так например, при наличии кратности «многие-ко-многим» придется создавать вспомогательную таблицу. Используйте специфические стереотипы классов и пометки атрибутов (для задания первичных и вторичных ключей, например) [uml_datamodeling];
  3. решить проблемы использования полученной диаграммы в качестве физической модели базы данных — циклические ассоциации, n-арные ассоциации и т.д. При необходимости создать промежуточные абстракции;
  4. раскрыть операции, важные для доступа к данным и поддержания целостности;

Заключение и список литературы

В статье я постарался описать наиболее существенные элементы диаграммы классов, а также аспекты их применения. Просматривается, что диаграмма строится на начальном этапе проектирования (концептуальная модель) и является его результатом. На всех этапах проектирования созданная в начале диаграмма классов дорабатывается, т.е. я рассматриваю итеративный процесс (такой как RUP или ICONIX). Кроме того, показано, использование диаграммы классов в других целях — эскизирования, документирования, моделирования логической схемы БД. На других страницах этого блога вы можете найти множество примеров использования диаграммы классов.

  • диаграмма шаблона проектирования Publish-Subscriber [pattern_mvc];
  • множество диаграмм, демонстрирующих шаги рефакторинга для приведения проекта в соответствие принципам SOLID [solid_refactoring];
  • диаграммы классов для паттерна адаптер и сетевого чата [pattern_adapter].
  • [Buch] Буч Градди Объектно-ориентированный анализ и проектирование с примерами приложений, 3-е изд. / Буч Градди, Максимчук Роберт А., Энгл Майкл У., Янг Бобби Дж., Коналлен Джим, Хьюстон Келли А.: Пер с англ. — М.: ООО «И.Д. Вильямс», 2010. — 720 с.
  • [Leonenkov] Леоненков, А.В. Самоучитель UML 2 / А.В. Леоненков. – СПб.: БХВ — Петербург, 2007. – 576с.
  • [Larman] Ларман, К. Применение UML и шаблонов проектирования: Уч. Пос / К. Ларман. — М.: Издательский дом «Вильямс», 2001. — 496 с.
  • [Rosenberg]Розенберг Д., Скотт К. Применение объектного моделирования с использованием UML и анализ прецедентов.: Пер. с англ. М.: ДМК Пресс, 2002
  • [Badd] Бадд Т. Объектно-ориентированное программирование в действии: Пер с англ. — СПб: «Питер», 1997. — 464 с. 2002
  • [Buch_Rambo] Буч Г., Рамбо Д., Джекобсон А. Язык UML. Руководство пользователя: Пер. с англ. — М.: ДМК, 2000. — 432 с.
  • [Fauler] Фаулер, М. UML. Основы / М. Фаулер, К. Скотт; пер. с англ. – СПб.: Символ – Плюс, 2002. – 192 с.
  • [solid_refactoring] SOLID принципы. Рефакторинг — URL: https://pro-prof.com/archives/1914
  • [uml_sequence_diag]Основы UML. Диаграммы последовательности — URL: https://pro-prof.com/archives/2769
  • [uml_datamodeling] UML data modeling — URL: http://www.agiledata.org/essays/umlDataModelingProfile.html
  • [doxygen_codegeneration] Использование doxygen — URL: https://pro-prof.com/archives/887
  • [pattern_mvc] Паттерны MVC и Publish-Subscriber — URL: https://pro-prof.com/archives/2400
  • [pattern_adapter] Работа с сетью в Qt. Сокеты. Паттерн Adapter — URL: https://pro-prof.com/archives/1372

0 0 голоса
Рейтинг статьи
Подписаться
Уведомить о
guest

0 комментариев
Старые
Новые Популярные
Межтекстовые Отзывы
Посмотреть все комментарии

А вот еще интересные материалы:

  • Яшка сломя голову остановился исправьте ошибки
  • Ясность цели позволяет целеустремленно добиваться намеченного исправьте ошибки
  • Ясность цели позволяет целеустремленно добиваться намеченного где ошибка
  • Приложение для чтения ошибок рено
  • Применение инструмента невозможно ошибка диска