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

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

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

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

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

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

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

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

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

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

Роль может быть явно поименована с помощью метки. Например, роль ассоциации в направлении от Заказа к Строкам заказа называется «позиции заказа». Если такая метка отсутствует, роли присваивается имя класса-цели – таким образом, роль ассоциации от Заказа к Клиенту может быть названа Клиент (термины «начало» (source) и «цель» (target) употребляются для обозначения классов, являющихся соответственно начальным и конечным для ассоциации).

Роль также обладает множественностью, которая показывает, сколько объектов может участвовать в данной связи. На рисунке символ «*» над ассоциацией между Клиентом и Заказом означает, что с одним Клиентом может быть связано много Заказов; символ «1» показывает, что любой Заказ может поступить только от одного Клиента.

В общем случае множественность указывает нижнюю и верхнюю границы количества объектов, которые могут участвовать в связи. Символ «*» в действительности выражает диапазон «ноль-бесконечность»: Клиент может и не сделать ни одного Заказа, а может сделать неограниченное количество Заказов (теоретически). Единица означает диапазон «один-один»: Заказ должен быть сделан одним и только одним Клиентом.

На практике наиболее распространенными вариантами множественности являются «1», «*» и «0..1» (либо ноль, либо единица). В общем случае может использоваться единственное число (например, 11 для количества игроков в команде), диапазон (например, 2..4 для игроков в карты) или дискретная комбинация из чисел и диапазонов (например, 2,4 для количества дверей в автомобиле).

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Добавить комментарий