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

ПредисловиеПарадигма объектно-ориентированного программирования (далее просто ООП) повсеместно используется при создании современного программного обеспечения. М...

Предисловие

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

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

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

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

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

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

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

Использование ООП может существенно упросить жизнь программисту. Это достигается за счёт сокрытия особенностей внутренней реализации классов. Программисту остаётся лишь пользоваться её удобствами. Кажется, что ООП – панацея от всех проблем. Однако на практике, если не иметь чёткого представления о том, какие классы нужно реализовать и как ими потом пользоваться, в результате может получиться очень запутанная система, которая начнёт порождать спагетти-коду (от англ. “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, следует перейти к их последовательному рассмотрению.

Унифицированный язык моделирования ( 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 class diagram relationships

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Зависимости

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

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

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

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

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

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

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

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

Среди шести типов отношений кодовая структура комбинации, агрегации и ассоциации одинакова, и ее можно понять по силе отношения. Порядок от сильного к слабому: наследование → реализация → композиция → агрегация → ассоциация → зависимость . Ниже приведена полная диаграмма 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.
Класс в UML
является основной структурной единицей.
Диаграммы классов наиболее информационно
насыщены по сравнению с другими типами
канонических диаграмм UML,
инструменты генерируют код в основном
по описанию классов, структура классов
точнее всего соответствует окончательной
структуре кода приложения.

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

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

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

  • раздел
    атрибутов — содержит список описаний
    атрибутов класса;

  • раздел
    операций — содержит список описаний
    операций класса.

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

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

Раздел
имени класса в общем случае имеет
следующий синтаксис.

«стереотип»
ИМЯ {свойства} кратность.

В
табл. 2 перечислены стандартные стереотипы
классов.

Таблица
2. Стандартные
стереотипы классов

Стереотип

Описание

actor

действующее
лицо

enumeration

перечислимый
тип данных

exception

сигнал,
распространяемый по иерархии обобщений

implementation
Class

реализация
класса

interface

нет
атрибутов и все операции абстрактные

metaclass

экземпляры
являются классами

powrtype

метакласс,
, экземплярами которого являются все
наследники данного класса

process,
thread

активные
классы

signal

класс,
экземплярами которого являются
сообщения

stereotype

стереотип

type
(datatype)

тип
данных

utility

нет
экземпляров
= служба

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

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

Кратность
класса задается по общим правилам.
Наиболее распространенный случай
неограниченной кратности (т. е. класс
может иметь произвольное значение
экземпляров) подразумевается по умолчанию
и никак не отражается на диаграмме
классов. Другой распространенный случай
— нулевая кратность — обычно представляется
с помощью стандартного стереотипа
«utility»
(см.
табл. 2).

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

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

Рисунок
3.3. Раздел имени службы

Атрибут
— это именованное место (или, как говорят,
слот),
в котором может храниться значение.

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

видимость
ИМЯ кратность : тип = начальное значение
{свойства}

Видимость,
как обычно, обозначается знаками +, -, #.
Еще раз подчеркнем, что если видимость
не указана, то никакого значения видимости
по умолчанию не подразумевается.

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

Подчеркивание
описания атрибута соответствует
описателю static
в языке С++. Кратность,
если она присутствует, определяет данный
атрибут как массив (определенной или
неопределенной длины).

Тип
атрибута — это либо примитивный
(встроенный) тип, либо тип определенный
пользователем.

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

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

У
атрибутов имеется еще одно стандартное
свойство: изменяемость.
В табл. 3 перечислены возможные значения
этого свойства.

Таблица
3.
Значения
свойства изменяемости атрибута

Значение

Описание

changeable

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

addOnly

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

frozen

Значение
атрибута задается при инициализации
объекта и не может меняться

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

Таблица
4.
Примеры
описаний атрибутов

Пример

Пояснение

name

Минимальное
возможное описание – указано только
имя атрибута

+name

Указаны
имя, тип и открытая видимость —
предполагается, манипуляции с именем
будут проводиться непосредственно

-name:String

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

-name[1..3]:String

Указана
кратность (для хранения трех составляющих:
фамилии, имени, отчества)

-name:String
= “Novikov”

Указано
начальное значение

+name:String
{frozen}

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

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

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

Описания
операций класса перечисляются в разделе
операций и имеют следующий синтаксис:

видимость
ИМЯ (параметры) : тип {свойства}

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

направление
ПАРАМЕТР : тип = значение

Видимость,
как обычно, обозначается с помощью
знаков +, -, #.5
Подчеркивание имени означает, что
область действия операции — класс, а
не объект. Например, конструкторы имеют
область действия класс. Курсивное
написание имени означает, что операция
абстрактная, т. е. в данном классе ее
реализация не задана и должна быть
задана в подклассах данного класса.
После имени в скобках может быть указан
список описаний параметров. Описания
параметров в списке разделяются запятой.
Для каждого параметра обязательно
указывается имя, а также могут быть
указаны направление передачи параметра,
его тип и значение аргумента по умолчанию.

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

Таблица
5.
Ключевые
слова для описания направления передачи
параметров

Ключевое
слово

Назначение
параметра

In

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

Out

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

Inout

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

Return

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

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

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

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

Во-первых,
это concurrency

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

Таблица
6.
Значения
свойства concurrency

Ключевое
слово

Назначение
параметра

In

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

Out

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

Inout

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

Return

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

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

Если значение данного свойства true,
то выполнение операции не меняет
состояния системы — операция только
вычисляет значения, возвращаемые в
точку вызова.6
В противном случае, т. е. при значение
false,
операция меняет состояние системы:
присваивает новые значения атрибутам,
создает или уничтожает объекты и т. п.
По умолчанию операция имеет свойство
{isQuery=false}.
Поэтому, если нужно указать, что данная
операция — это функция без побочных
эффектов, то достаточно написать
{isQuery}.
Рассмотрим примеры описания возможных
операций класса Person
информационной системы отдела кадров
(табл. 7).

Таблица
7.
Примеры
описания операций

Пример

Пояснение

move

Минимально
возможное описание – указано только
имя операции

+move(in
from : Dpt, in to)

Указаны
видимость операции, направления
передачи и имена параметров

+move(in
from : Dpt, in to: Dpt)

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

+getName()
: String {isQuery}

Функция,
возвращающая значение атрибута и не
имеющая побочных эффектов

+setPwd(in
pwd : String = “password”)

Процедура,
для которой указано значение аргумента
по умолчанию

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

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

  • между
    пакетами;

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

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

  • между
    состояниями автомата;

  • между
    подсистемами и моделями.

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

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

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

Стереотип

Применение

bind

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

derive

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

friend

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

instanceOf

Указывает,
что зависимый объект (или класс)
является экземпляром независимого
класса (метакласса)

instantiate

Указывает,
что операции независимого класса
создают экземпляры зависимого класса

powertype

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

refine

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

use

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

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

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

Рисунок
3.4 – Отношение обобщения

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

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

Тогда
данную ситуацию можно описать с помощью
диаграммы, приведенной на рисунке 3.5.
Классы Male
и Female
являются экземплярами метакласса
Gender.
Классы Owner
и Employe
являются экземплярами метакласса
Status.
Это видно из имен подмножеств обобщений,
указанных возле пунктирных линий,
выделяющих подмножества. Классификация
по полу является завершенной и дизъюнктной.
Классификация по отношению собственности,
напртив, не является ни завершенной, ни
дизъюнктной.

Рисунок
3.5. Подмножества обобщений и метаклассы

Иногда
бывает важно указать, что некоторый
объект является экземпляром конкретного
класса. Это делается с помощью стереотипа
instanceOf
(Рисунок
3.6). Далее, обычно конструктор объектов
класса определяется в классе. Но иногда
конструктор помещают в другой класс,
который в таком случае называют фабрикой.
На рисунке 3.6 объект (единственный)
класса Company
является
фабрикой объектов классов Department
и
Person,
а
объект класса Department
является
фабрикой объектов класса Position

Рисунок
3.6. Инстанциирование и спецификация
экземпляра

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

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

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

  • имя
    ассоциации (возможно, вместе с направлением
    чтения);

  • кратность
    полюса ассоциации;

  • вид
    агрегации полюса ассоциации;

  • роль
    полюса ассоциации;

  • направление
    навигации полюса ассоциации;

  • упорядоченность
    объектов на полюсе ассоциации;

  • изменяемость
    множества объектов на полюсе ассоциации;

  • квалификатор
    полюса ассоциации;

  • класс
    ассоциации;

  • видимость
    полюса ассоциации;

  • многополюсные
    ассоциации.

Начнем
по порядку. Имя
ассоциации

указывается в виде строки текста над
(или под, или рядом с) линией ассоциации.
Имя не несет дополнительной семантической
нагрузки, а просто позволяет различать
ассоциации в модели. Обычно имя не
указывают, за исключением многополюсных
ассоциаций или случая, когда одна и та
же группа классов связана несколькими
различными ассоциациями. Например, в
информационной системе отдела кадров,
если сотрудник занимает должность, то
соответствующие объектов классов Person
и Position
должны быть связаны. Дополнительно
можно указать направление чтения имени
ассоциации. Фрагмент графической модели,
приведенный на рисунке 3.7, фактически
можно прочитать вслух:

Рисунок
3.7. Имя ассоциации

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

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

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

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

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

Агрегация
и композиция.

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

Агрегация
от класса A
к классу B
означает, что объекты (один или несколько)
класса A
входят в состав объекта класса B.

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

Рисунок
3.10. Агрегация

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

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

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

Рисунок
3.11. Сравнение композиции (вверху) и
агрегации (внизу)

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

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

Роль
полюса ассоциации,

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

— это способ указать, как именно участвует
классификатор (присоединенный к данному
полюсу ассоциации) в ассоциации. В общем
случае данное дополнение имеет следующий
синтаксис:

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

Имя
является обязательным, оно называется
именем
роли

и фактически является собственным
именем полюса ассоциации, позволяющим
различать полюса. Если рассматривается
одна ассоциация, соединяющая два
различных класса, то в именах ролей нет
нужды: полюса ассоциации легко можно
различить по именам классов, к которым
они присоединены. Однако, если это не
так, т. е. если два класса соединены
несколькими ассоциациями, или же если
ассоциация соединяет класс с самим
собой, то указание роли полюса ассоциации
является необходимым. Такая ситуация
отнюдь не является надуманной, как может
показаться. Вернемся к рисунку 3.12, где
мы, забегая вперед, начали разбор примера
на эту тему. На этом рисунке изображена
ассоциация класса Position
с самим собой. Эта ассоциация призвана
отразить наличие иерархии подчиненности
должностей в организации. Однако из
рисунка 3.12 видно только, что объекты
класса Person
образуют некоторую иерархию (каждый
объект связан с некоторым количеством
нижележащих в иерархии объектов и не
более чем с одним вышележащим объектом),
но не более того. Используя спецификацию
интерфейсов и, заодно, (опять несколько
забегая вперед) отношения реализации
и интерфейсы можно описать субординацию
в информационной системе отдела кадров
достаточно лаконично, но точно. Например,
на рисунке 3.13 указано, что в иерархии
субординации каждая должность может
играть две роли. С одной стороны, должность
может рассматриваться как начальственная
(chief),
и в этом случае она предоставляет
интерфейс IChief9
имеющий операцию petition
(начальнику можно подать служебную
записку). С другой стороны, должность
может рассматриваться как подчиненная
(subordinate),
и в этом случае она предоставляет
интерфейс ISubordinate,
имеющий операцию report
(от подчиненного можно потребовать
отчет). У начальника может быть произвольное
количество подчиненных, в том числе и
0, у подчиненного может быть не более
одного начальника.

Рисунок
3.13. Роли полюсов ассоциации

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

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

Направление
навигации

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

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

Рассмотрим
следующий пример (который может быть
полезен и в информационной системе
отдела кадров, если понадобиться
расширить ее функциональность защитой
данных на уровне пользователя). Допустим,
имеются два класса: User
(содержит
информацию о пользователе) и Password
(содержит
пароль — информацию, необходимую для
аутентификации пользователя). Мы хотим
отразить в модели следующую ситуацию:
имеется взаимно однозначное соответствие
между пользователями и паролями, зная
пользователя можно получить доступ к
его паролю, но обратное неверно: по
имеющемуся паролю нельзя определить,
кому он принадлежит. Такая ситуация
может быть промоделирована, как показано
на рисунке 3.15.

Рисунок
3.15. Направление навигации

Видимость
полюса ассоциации

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

Продолжим
рассмотрение предыдущего примера.
Допустим, у нас есть третий класс —
Workgroup

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

Рисунок
3.16. Ограничение видимости полюса
ассоциации

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

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

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

Ели кратность полюса лежит в диапазоне
0..1, то проблемы упорядоченности не
возникает — на данном полюсе связи,
возможно, имеется один объект, но не
более того. При иных кратностях объектов
может быть несколько, т. е. полюс связи
присоединен к множеству объектов. По
умолчанию множество объектов,
присоединенных к данному полюсу связи,
считается неупорядоченным (как и любое
множество, если не оговорено противное).
Если необходимо указать, что это множество
упорядочено (см. пример ниже и рисунок
3.17), то нужно наложить соответствующее
ограничение на полюс ассоциации. В
качестве ограничения используется одно
из двух ключевых слов:

  • ordered
    (или sorted)
    если множество упорядочено;

  • unordered,
    если
    не упорядочено.

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

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

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

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

Квалификатор
полюса ассоциации

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

Квалификатор
изображается в виде небольшого
прямоугольника на полюсе ассоциации,
примыкающего к прямоугольнику класса.
Внутри этого прямоугольника (или рядом
с ним) указывается имена и, возможно,
типы атрибутов квалификатора. Описание
квалифицирующего атрибута ассоциации
имеет такой же синтаксис, что и описание
обычного атрибута класса, только оно
не может содержать начального значения.
Квалификатор может присутствовать
только на том полюсе ассоциации, который
имеет кратность «много», поэтому,
если на полюсе ассоциации с квалификатором
задана кратность, то она указывает не
допустимую мощность множества объектов,
присоединенных к полюсу связи, а
допустимую мощность того подмножества,
которое определяется при задании
значений атрибутов квалификатора.
Например, на рисунке 3.18 приведен фрагмент
модели для примера с многоугольниками,
в котором использован квалификатор (в
данном случае с именем number).
Обратите внимание, что на полюсе
ассоциации с квалификатором, присоединенном
к классу Point,
указана кратность 0..1
(а не *, как на Рисунок 3.17). Действительно,
само наличие квалификатора указывает
на кратность много, а кратность 0.. 1
указывает сколько вершин с конкретным
номером связано с данным многоугольником:
1, если задан допустимый номер вершины,
или 0 в противном случае.

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

Ассоциация
имеет экземпляры (связи), стало быть,
является классификатором и может
обладать соответствующими свойствами
и составляющими классификатора. В
распространенных случаях, рассмотренных
выше, ассоциации не обладали никакими
собственными составляющими. Грубо
говоря, ассоциация между классами A
и B
— это просто множество пар (a,b),
где а
— объект класса A,
а b
— объект класса B.
Подчеркнем еще раз, что это именно
множество: двух одинаковых пар (a,b)
быть не может. Однако возможны и более
сложные ситуации, когда ассоциация
имеет собственные атрибуты (и даже
операции), значения которых хранятся в
экземплярах ассоциации — связях. В
таком случае применяется специальный
элемент моделирования — класс ассоциации.

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

— это сущность, которая имеет как
свойства класса, так и свойства ассоциации.

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

Вернемся
к информационной системе отдела кадров
и рассмотрим следующий пример. Допустим,
что имеет место более сложное отношение
между должностями и сотрудниками, нежели
те, что приведены на рисунке 3.10 и 3.11. А
именно, допускается не только совмещение
должностей (один сотрудник может работать
на нескольких должностях), но и дробление
ставок (одну должность могут занимать
несколько сотрудников — полставки,
четверть ставки и т. п.). Использую уже
разобранную нотацию ассоциации, мы
можем констатировать, что между классами
Person
и
Position
имеет
место ассоциация «многие ко многим».
Однако этого недостаточно: необходимо
указать, какую долю данной должности
занимает данный сотрудник. Эту информацию
нельзя отнести ни к должности, ни к
сотруднику — это атрибут класса
ассоциации между ними (Рисунок 3.19).

Рисунок
3.19.
Класс
ассоциации

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

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

На
первый взгляд, модели на рисунке 3.19 и
3.20 выглядят семантически эквивалентными,
однако это не так — здесь есть тонкое
различие. В случае использования класса
ассоциации Job,
который по определению является
множеством пар должность-сотрудник, не
может быть двух одинаковых пар. То есть
не может быть так, чтобы Новиков занимал
полставки доцента и еще (отдельно)
четверть той же ставки. В случае же
промежуточного класса Job
это, вообще говоря, вполне возможно, что
не совсем естественно. Опытные
проектировщики баз данных нам могут
возразить, что это вполне поправимо:
нужно ввести в классах Position
и Person
атрибуты, которые будут уникальными
ключами, идентифицирующими объекты
этих классов, а в классе Job
ввести пару атрибутов, значениями
которых будут ключи классов Position
и Person
и потребовать, чтобы эта пара атрибутов
была уникальным составным ключом класса
Job.

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

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

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

Рисунок
3.22. Метамодель ассоциации

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

Интерфейс
— это именованный набор абстрактных
операций.

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

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

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

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

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

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

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

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

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

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

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

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

Здесь
уместно дать точные ответы на два важных
вопроса.

  • Для
    каких элементов модели можно указать
    тип?

  • Что
    можно использовать в качестве указания
    типа?

В
UML
типизированы могут быть:

  • атрибуты
    классов, в том числе классов ассоциаций;

  • параметры
    операций, в том числе тип возвращаемого
    значения;

  • роли
    полюсов ассоциаций;

  • квалификаторы
    полюсов ассоциаций;

  • параметры
    шаблонов.

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

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

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

  • Примитивные
    типы, которые считаются предопределенными
    в UML
    — таковых, как минимум, три: строка,
    целое число и значение даты/времени.
    Инструменты вправе расширять этот
    набор и использовать подходящие
    названия.

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

  • Типы
    данных, которые определены в модели. В
    стандарте UML
    предусмотрен только один конструктор
    типов данных: перечислимый тип, который
    определяется с помощью стереотипа
    «enumeration».

В
частности, тип Boolean
определен в UML
как перечислимый тип со значениями true
и false.
Приведем пример: допустим, в нашем
приложении нужно использовать не обычную
двузначную логику, а трехзначную. Тогда
соответствующий можно определить так,
как показано на рисунке 3.25.

Рисунок
3.25. Перечислимый тип данных «Трехзначная
логика»

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

Возникает
вопрос: чем же типы данных отличаются
от прочих классификаторов UML?

Тип
данных


UML)
— это классификатор, экземпляры которого
не обладают индивидуальностью (identity).

Это
довольно тонкое понятие, которое мы
попробуем объяснить на примере. Рассмотрим
какой-нибудь встроенный тип данных в
обычном языке программирования, например,
тип integer
в языке Паскаль. Значения этого типа
(экземпляры классификатора) изображаются
обычным образом, например, 3. Что будет,
если число «три» используется в
нескольких местах программы? Отличатся
ли чем-нибудь экземпляры изображения
3? Очевидно, нет. Экземпляры типа integer
не обладают индивидуальностью, мы вправе
считать, что написанные в разных местах
изображения числа 3 суть одно и то же
число, а компилятор вправе использовать
для хранения представления числа «три»
одну и ту же ячейку, сколько бы изображений
числа 3 ни присутствовало в программе.
Далее, программисту не нужно предусматривать
никаких инициализирующих действий, для
того, чтобы воспользоваться числом 3 —
не нужно определять никаких объектов,
не нужно вызывать никаких конструкторов.
Можно считать, что все значения типа
данных уже определены и всегда существуют,
независимо от программы. Более того, с
числом «три» ничего не может
случиться — чтобы ни происходило в
программе, число «три» останется
числом «три» и никогда не станет
числом » пять».

Сопоставим
сказанное с обычным классом, экземпляры
которого обладают индивидуальностью.
Допустим, в классе Cinteger
есть только один атрибут, который хранит
целое значение. На первый взгляд, такой
класс ничем не отличается от типа данных
integer
— экземпляры данного класса вполне
можно использовать
как целые числа. Но это поверхностное
впечатление: между типом данных integer
и
классом Cinteger
много
существенных отличий. Во-первых,
экземпляры класса Cinteger
должны
быть явно созданы и инициализированы,
прежде чем их можно будет использовать
в программе. Во-вторых, экземпляр класса
Cinteger,
который в данный момент хранит число
«три», через некоторое время
выполнения программы может хранить
число «пять», оставаясь при этом
тем
же самым

экземпляром, поскольку он обладает
индивидуальностью. В- третьих, в программе
может быть определено несколько
экземпляров класса Cinteger,
которые хранят одно и то же число «три»
и это будут разные объекты (компилятор
разместит их в разных областях памяти),
поскольку они обладают индивидуальностью.

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

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

  • Операции
    типа данных всегда обладают свойством
    isQuery.

  • Операции
    типов данных считаются не повторно
    входимыми и обладают свойством sequential
    .

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

ИМЯ
: тип

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

  • явное
    связывание

    — зависимость со стереотипом «bind»,
    в которой указаны значения аргументов;

  • неявное
    связывание

    — определение класса, имя которого
    имеет формат

имя_шаблона
< аргументы >

Рассмотрим
пример (Рисунок 3.26). Здесь определен
шаблон Array,
имеющий два параметра: n
типа Integer
и T,
тип которого не указан. Этот шаблон
применяется для создания массивов
определенной длины, содержащих элементы
определенного типа. В данном случае с
помощью явного связывания определен
класс Triangle
как массив из трех элементов типа Point.
С помощью неявного связывания определен
аналогичный по смыслу класс с именем
Array<3,Point>.

Рисунок
3.26. Связывание шаблона: неявное (вверху),
явное (внизу)

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

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

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

  • Не
    обязательно включать в модель все
    классы сразу. На первых итерациях
    достаточно идентифицировать очень
    небольшую (10%) долю всех классов системы.

  • Не
    обязательно определять все свойства
    класса сразу. Начните с имени — операции
    и атрибуты постепенно выявятся в
    процессе моделирования поведения.

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

  • Не
    обязательно определять все отношения
    между классами сразу. Пусть класс на
    диаграмме «висит в воздухе» —
    ничего с ним не случится.

Сущности на диаграмме классов

Классификатор — это дескриптор множества однотипных объектов

→ может иметь экземпляры

действующее лицо (actor), вариант использования (use case), артефакт (artifact), тип данных (data type), ассоциация (association), класс ассоциации (association class), интерфейс (interface), класс (class), кооперация (collaboration), компонент (component), узел (node) и др., (сигнал (signal), … ) применяющиеся в процессе моделирования поведения.

Свойства классификаторов

1. Имя

2. Экземпляры

3. Абстрактный

  • не может иметь прямых экземпляров
  • имя выделяется курсивом

4. Видимость

  • может ли составляющая одного классификатора использоваться в другом
  • открытый        + public        — виден везде
  • защищенный  # protected  — виден в контейнере
  • закрытый        — private       — виден в своем элементе
  • пакетный        ∼package    — виден в своем пакете

5. Область действия

  • определяет, как проявляет себя составляющая классификатора в экземплярах: используется общее значение или у каждого экземпляра свое
  • экземпляр (instance) — по умолчанию
  • классификатор (classifier) — подчеркивается

6. Кратность

  • Все допустимые значения мощности Нижняя граница … ВЕРХНЯЯ ГРАНИЦА
  • кратность 0 нет экземпляров служба (utility)
  • кратность 1 ровно один одиночка (singleton)
  • кратность 8 фиксированное число (e. g. концентратор)
  • кратность * произвольное число по умолчанию

7. Могут участвовать в отношении обобщения

Сущности на диаграмме классов

  • Классы
    • стереотипы
    • интерфейсы
    • типы данных
    • активные классы
  • Пакеты
  • Примечания

Класс — один из самых богатых элементов моделирования UML.

Секции:

  • Секция имени
    «стереотип» ИМЯ {свойства} кратность
  • Секция атрибутов
    • список описаний атрибутов
  • Секция операций
    • список описаний
    операций
    + доп. секции = примечания

Атрибут — именованное место, в котором может храниться какое-то значение.

видимость ИМЯ [кратность] : тип = начальное значение {свойства}

  • Видимость: + — # ∼
  • ИМЯ: атрибут экземпляра, ИМЯ: атрибут класса
  • Кратность: определяет атрибут как массив
  • Тип: примитивный / определенный пользователем
  • Начальное значение: при создании экземпляра атрибут получает указанное значение
  • Свойства: ограничения и именованные значения, e.g. изменяемость (changeability)

Примеры описания атрибутов

Операции и методы

Операция — спецификация действия с объектом.

Метод — выполняемый алгоритм.

  • Видимость: + — # ∼
  • ИМЯ: операция экземпляра, ИМЯ: операция класса, ИМЯ: абстрактная операция
  • Параметры:
    направление ПАРАМЕТР : тип = значение

Свойства операций

1. Параллелизм — когда имеется несколько потоков управления

2. Побочные эффекты

  • True — не меняет состояния системы
  • False (по умолчанию) — операция меняет состояние системы:
    присваивает новые значения атрибутам, создает или уничтожает объекты и т. п.

Примеры описания операций

Параметры

  • Терминология:
    • параметры — формальные
    • аргументы — фактические

  • Направления передачи параметров в UML:

Интерфейсы и типы данных

Интерфейс — это именованный набор абстрактных составляющих.
Тип данных — это:

  • множество значений (потенциально бесконечное)
  • конечное множество операций
  • значения не обладают индивидуальностью

Примеры типов данных

Тип данных (в UML) — это классификатор, экземпляры которого не обладают индивидуальностью.

Идентификация классов

1. Словарь предметной области: существительные в ТЗ

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

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

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

3. Образцы проектирования

Отношения на диаграмме классов

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

Стандартные стереотипы зависимости между классами и объектами

Отношение реализации

Классификатор использует интерфейс — зависимость со стереотипом «call» (требуемый интерфейс).

Классификатор реализует интерфейс — реализация (обеспеченный интерфейс).

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

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

Применение обобщений:

  • Множественное наследование 
    • Класс является подклассом нескольких суперклассов
  • Несколько иерархий обобщения
    • Не требуется, чтобы у базовых классов был общий
    суперкласс
  • Ограничение: частичная упорядоченность
    • Отсутствие циклов в цепочках обобщений

Пример:

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

Отношение ассоциации
Ассоциация в UML — это классификатор, экземпляры которого называются связями (link)

  • Минимум:
    Объекты ассоциированных классов могут взаимодействовать во время выполнения

Имя ассоциации

Между классами должно быть отношение ассоциации (1) и может быть имя (2), поясняющее ее назначение. Дополнительно можно указать направление чтения имени ассоциации (3).

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

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

* — неопределенное число

Агрегация и композиция
Агрегация — это ассоциация между классом А (часть) и классом В (целое)

  • Не изменяет направления навигации и не накладывает ограничений на время жизни

Композиция — более сильные ограничения:

  • Часть А может входить только в одно целое В
  • Время жизни частей совпадает с временем жизни целого

Примеры агрегации и композиции

Структура элементов системы

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

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

Ассоциация класса Position самим с собой (1). На полюсах ассоциации указаны роли (2). Значок, показывающий направление чтения (3) позволяет прочесть данную ассоциацию как “Chief subordinates Subordinate”. Эта ассоциация призвана отразить наличие иерархии подчиненности должностей в организации.

Уточнение предыдущей схемы:

Направление навигации

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

Многополюсные ассоциации

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

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

Ограничения на полюсах ассоциации
Упорядоченность объектов (ordering) / уникальность (uniqueness) — наличие / отсутствие одинаковых объектов

Упорядоченность и изменяемость

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

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

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

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-диаграмма вам больше всего подходит. Если у вас есть вопросы или предложения, не стесняйтесь оставлять комментарии.

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

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

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

Способы отображения класса

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

Пример класса без атрибутов (интерфейса)

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

Обязательным элементом обозначения класса на диаграмме является его имя. Оно должно быть уникальным в пределах пространства имён (namespace). Если класс является абстрактным, то его имя пишется курсивом. Абстрактный класс – это класс, на основе которого нельзя создать объекты. Такие классы используются в качестве шаблона для дочерних классов при наследовании.

В секции имени класса может быть указан стереотип (например, «entity», «boundary», «interface» и т.п.).

Во второй секции каждому атрибуту соответствует отдельная строка со следующей спецификацией:

[видимость] [/] имя [: тип [‘[‘кратность‘]‘] [ = исходное значение]] [‘{‘модификаторы’}’].

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

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

  • «+» – общедоступный атрибут (англ. public) – доступен для чтения и модификации из объектов любого класса;

  • «#» – защищенный атрибут (англ. protected) – доступен только объектам описываемого класса и его потомкам при наследовании;

  • «–» – закрытый атрибут (англ. private) – доступен только объектам описываемого класса;

  • «~» – пакетный атрибут (англ. package) – доступен только объектам классов, входящих в тот же пакет.

Символ «/» перед именем атрибута указывает на то, что он является производным (т.е. его значение вычисляется из значений других атрибутов или ассоциаций).

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

Тип (англ. type) атрибута выбирается исходя из семантики значений, которые должны храниться в атрибуте, и, как правило, возможностей целевого языка программирования по представлению этих значений. Он соответствует одному из стандартных типов, определенных в этом языке (например, String, Boolean, Integer, Color и т. д.) или имени класса, на объект которого в этом атрибуте будет храниться ссылка. Во втором случае класс, имя которого указано в качестве типа, должен быть определен на диаграмме или в модели.

Кратность (англ. multiplicity) атрибута характеризует количество значений, которые можно хранить в атрибуте. Если кратность атрибута не указана, то по умолчанию принимается ее значение, равное 1, т. е. атрибут является атомарным. Такой вариант допускает и отсутствие значения в атрибуте (null). Для атрибута, представляющего собой массив, множество, список и т. п., требуется указание кратности, которая записывается после типа в квадратных скобках. Варианты указания кратности, имеющие смысл, могут быть следующие:

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

  • [0..<число>] – количество хранимых значений, может быть не более указанного числа. Данный вариант применяется при описании массивов фиксированного размера. При этом не обязательно, чтобы все элементы массива имели конкретные значения;

  • [0..<число>] [0..<число>] – применяется при описании двумерных массивов. Аналогичным образом можно описать трехмерные, четырехмерные и т.д. массивы.

Исходное значение (англ. default value) служит для задания некоторого начального значения атрибута в момент создания отдельного экземпляра класса (объекта).

Mодификатор (англ. modifier) описывает особенности реализации атрибута, например:

  • {final} / {readOnly} – атрибут является константой, т.е. доступен только для чтения;

  • {static} – атрибут при выполнении программы в конкретный момент времени будет иметь одно и то же значение для всех объектов класса;

  • {transient} – атрибут и его значение при записи объекта в БД или файл (сериализации объекта) не должны запоминаться;

  • {redefines <имя атрибута родительского класса>} – атрибут переопределяет (заменяет) атрибут родительского класса;

  • {id} – значение атрибута используется в качестве идентификатора объекта класса;

  • {unique} или {nonunique} – значения неатомарного атрибута должны быть уникальны или допускаются повторы значений;

  • {ordered} или {unordered} – значения неатомарного атрибута должны быть отсортированы или могут содержаться в произвольном порядке;

  • {seq} / {sequence} – значения неатомарного атрибута хранятся упорядочено (к ним можно обращаться по индексу или выполнять перебор в соответствии с порядком их добавления в список/массив/множество) и могут повторяться.

Допускается указывать несколько модификаторов через запятую. Например, {unique, ordered} означает, что элементы массива должны быть уникальны и следовать в строго определенном порядке (например, по возрастанию значений).

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

Спецификация атрибута в UML Генерируемый код для языка Java
+ name : String public String name;
+ pi : double = 3.1415 {final, static} public final static double pi = 3.1415;
– coordinateXY : int[][] = {{1, 1}, {2, 4}, {3, 9}} private int[][] coordinateXY = {{1, 1}, {2, 4},{3, 9}};
# visible : boolean = true protected boolean visible = true;
– connect : ConnectDB = null; private ConnectDB connect = null;

В Java типы, указанные с прописной буквы, называются примитивными (например, double или boolean). Значения атрибутов такого типа непосредственно хранится в объекте. Типы, указанные с заглавной буквы, называются ссылочными (например, String или ConnectDB). В атрибуте такого типа хранится ссылка на объект, созданный на базе соответствующего класса.

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

  • конструктор – метод, создающий и инициализирующий объект. В Java имя конструктора совпадает с именем класса;

  • деструктор – метод, уничтожающий объект. В некоторых языках программирования (в частности в Java) определение деструкторов не требуется, так как очистка памяти от неиспользуемых объектов (сборка мусора) выполняется автоматически;

  • модификатор – метод, который изменяет состояние объекта (значения атрибутов). Имена модификаторов начинаются, как правило, со слова set (англ. – установить). Например, установить атрибуту Name новое значение setName(newName : String);

  • селектор – метод, который может только считывать значения атрибутов объекта, но не изменяет их. Имена селекторов начинаются, как правило, со слов get (англ. – получить) или is при возврате логического результата. Например, считать значение атрибута Name – getName() или определить видимость на экране элемента графического интерфейса – isVisible();

  • итератор – метод, позволяющий организовать доступ к элементам объекта. Например, для объекта, представляющего собой множество Set или список List, это могут быть методы перехода к первому элементу first(), следующему next(), предыдущему previous() и т. п.;

  • событие – метод, запускаемый на выполнение автоматически при соблюдении определенных условий.

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

Каждому методу соответствует отдельная строка со следующей спецификацией:

[видимость] имя ([список параметров]) [: тип] [‘{‘свойства’}’].

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

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

имя : тип [‘[‘кратность‘]‘] [ = значение по умолчанию] [‘{‘строка-свойство’}’].

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

Тип параметра – тип значений, которые может принимать параметр.

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

Тип метода – тип результата, возвращаемого методом. Если тип не указан, то метод не возвращает никакого результата (в языках программирования такие методы, как правило, обозначаются модификатором void).

Свойства служат для указания специфических свойств метода, например:

  • {native} – реализация метода зависит от платформы (операционной системы);

  • {abstract} – метод в описываемом классе не имеет тела. Код метода должен быть определен в дочерних классах;

  • {sequential} – метод допускает только последовательный вызов. Парралельный вызов операции может вызвать сбой программы;

  • {guarded} – метод автоматически блокируется (ждет очереди) до завершения других вызовов (экземпляров) метода;

  • {concurrent} – допускается параллельное (одновременное) выполнение нескольких вызовов (экземпляров) метода;

  • {query} – метод не меняет состояние системы. Как правило, в языках программирования имена таких методов начинаются на get или is;

  • {redefines <имя метода родительского класса>} – метод переопределяет (заменяет) метод родительского класса;

  • {unique} или {nonunique} – возвращает множество значений без или с повторами;

  • {ordered} или {unordered} – возвращает отсортированное или неотсортированное множество значений;

  • {seq} / {sequence} – возвращает упорядоченное множество значений.

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

Спецификация метода в UML Генерируемый код для языка Java
«constructor» + TextFieldInt(value : int, length : int, alligment : int, fontField : Font) public TextFieldInt(int value, int length, int alligment, Font fontField) { }
+ saveData() public void saveData() {return;}
+ isVisible() : boolean public boolean isVisible() {return false;}
# init(text : String, icon : Icon) protected void init(String text, Icon icon) {return;}

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

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

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

Интерпретация ассоциации в тексте программы

Графический символ класса Class_A преобразуется в строки определения самого класса «public class Class_A» и его конструктора «public Class_A() {}». Аналогично для Class_B. Ассоциация от Class_B в сторону Class_A преобразуется в строку «public Class_A object_A;», описывающую атрибут object_A, в котором будет храниться ссылка на объект класса Class_A. Ввиду отсутствия указания кратности отношения, она по умолчанию принимается равной 1. На следуюшей рисунке приведен пример двунаправленной ассоциации кратностью более 1.

Интерпретация двунаправленной ассоциации

Наличие двунаправленной ассоциации или ассоциации без стрелок свидетельствует о наличии в обоих классах атрибутов, содержащих ссылки на объекты. Кратность более 1 подразумевает хранение не одной, а нескольких ссылок. Таким образом, один объект класса Class_A будет связан с несколькими объектами класса Class_B. Ссылки на эти объекты будут храниться в массиве object_B[]. Современные Case-средства позволяют вместо массива указывать другие варианты хранения набора объектов, такие как множества, списки, хешированные таблицы и т.д.

Явное указание отсутствия ссылок на объекты другого класса может задаваться значком «х».

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

Интерпретация композиции (агрегации)

Отношение обобщения в тексте программы на языке Java показывается ключевым словом «extends» (англ. – расширяет) в дочернем классе.

Интерпретация обобщения

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

На следующем рисунке показан условный пример, свидетельствующий о наличии зависимости класса Class_B от класса Class_A. В строке «public obrabotka(Class_A object_A)» используется ссылка на объект класса Class_A. В строке «String name = object_A.name;» выполняется обращение к атрибуту объекта класса Class_A. В строке «int age = object_A.getAge();» выполняется обращение к методу объекта класса Class_A.

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

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

Отношение реализации — дополнительное отношение на диаграмме классов по сравнению с диаграммой классов анализа, которое отображается только между классами и интерфейсами. В тексте на языке Java данное отношение обозначается ключевым словом «implements» (англ. – реализует).

Интерпретация реализации

Внешний вид отношения подчеркивает тот факт, что оно сочетает в себе особенности обобщения (наследования) и зависимости.

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

Связь классов через интерфейс

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

Пример объекта

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

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

  1. За основу диаграммы классов при ее разработке берется диаграмма классов анализа.

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

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

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

  5. Для проектирования классов-сущностей можно применять подходы, используемые при проектировании БД, особенно в том случае, если данные будут храниться в таблицах БД. Если представление данных в БД и классах отличается друг от друга и в качестве хранилища информации будет применяться реляционная база данных, то рекомендуется разработать отдельную диаграмму классов, описывающую состав и структуру БД. Современные Case-средства позволяют разрабатывать такие диаграммы и синхронизировать их с БД.

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

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

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

  9. Для атрибутов рекомендуется назначать видимость private (закрытый) или protected (защищенный). Если требуется чтение значения такого атрибута из объектов других классов, то следует предусмотреть для него get-метод, а если возможность установки нового значения – set-метод.

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

  1. Для методов видимость public (общедоступный) следует устанавливать только в случае крайней необходимости.

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

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

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

На следующем рисунке показан фрагмент диаграммы классов.

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

Данная диаграмма отражает структуру классов и связи между ними для пакета ru.iskraPUT.window системы ИСКРА-ПУТЬ. В диаграмму также включен класс InternalWindow из пакета ru.library.window, на базе которого определена половина классов пакета ru.iskraPUT.window. В правом верхнем углу части классов показаны стандартные классы языка Java, на основе которых они определены путем наследования.

Класс PutFame представляет собой главное окно программы, из которого вызываются внутренние диалоговые окна (классы, имя которых начинается на «Window»). Объект данного класса содержит ссылки на объекты, представляющие соответствующие окна, в связи с чем между ними на диаграмме показаны ассоциации.

В Together Architect при выборе класса на диаграмме отображается заголовок каждой секции. Так, для класса WindowSetting показаны:

  • Attributes (англ. – атрибуты) – секция атрибутов;

  • Operations (англ. – операции) – секция операций;

  • Properties (англ. – свойства) – секция свойств. Под свойствами понимаются атрибуты, для которых возможно чтение с помощью общедоступного (public) get-метода. Например, значение закрытого атрибута checkBoxVisibleWord можно считать с помощью открытого метода getCheckBoxVisibleWord();

  • Classes (англ. – классы) – секция внутренних классов, т. е. классов, определенных внутри рассматриваемого.

Статичные атрибуты (static) на диаграмме подчеркнуты.

Понравилась статья? Поделить с друзьями:
  • Прим 08ф ошибка 0030
  • Приложить силы лексическая ошибка
  • Приложить автобиографию стилистическая ошибка
  • Приложения сами устанавливаются на андроид как исправить
  • Приложения не отвечают на андроид что делать как исправить