Диаграмма классов (Class Diagram)
Модель предметной области отображает основные (с точки зрения проектировщика) классы понятий (концептуальные классы) в терминах предметной области. На языке UML модель предметной области представляется в форме диаграммы классов, на которой изображаются классы понятий реального мира, а не программных компонент. Кроме концептуальных классов модель предметной области может отображать ассоциации между классами и атрибуты концептуальных классов. Однако операции на диаграмме классов модели предметной области не отображаются.
Модель предметной области строится итеративно в течение нескольких итераций фазы развития. На каждой последующей итерации к модели добавляются новые концептуальные классы или уточняются имеющиеся классы очередного анализируемого варианта использования.
Наиболее простой прием идентификации концептуальных классов состоит в выделении существительных из текстового описания предметной области и их рассмотрения в качестве кандидатов в концептуальные классы или атрибуты.
Например, на рис. представлена диаграмма классов основного сценария варианта использования П.1 «Добавление файла УММ для обработки», где Спецификация загрузок УММ моделирует таблицу БД, в которую будут сохраняться данные (логин пользователя, дата, имя файла) о загружаемых на сервер файлах УММ. В Спецификации УММ для каждой УММ сохраняется атрибутивная информация (название УММ, год издания, кол-во страниц и т.д.), состав которой перечислен в ТЗ.
Рисунок — Диаграмма классов варианта использования «Добавить файл УММ для обработки»
На диаграмме класс изображается в виде прямоугольника, снабженного тремя внутренними зонами: имя класса, атрибуты, операции. Операция устанавливает форму функции или процедуры (ее сигнатуру). Конкретной реализацией операции является метод. В качестве имени класса следует использовать существительное в ед. числе.
Рисунок – Изображение класса на диаграмме классов
Объекты обмениваются сообщениями через соединения — связи. Но, для того, что бы между объектами была связь, между классами этих объектов должна существовать ассоциация, так как связь между объектами – это экземпляр ассоциации между классами.
Ассоциация – это связь между классами, отражающая некоторое значимое отношение между ними.
В процессе анализа ассоциаций необходимо придерживаться принципами минимализма, поскольку, если МПО будет состоять из N классов, то теоретически между ними можно будет установить N(N-1). В этой связи, в модель включают только те ассоциации, знания о которых необходимо сохранять в течение некоторого периода.
Не только ассоциация в целом может иметь имя, но и классам на обоих концах ассоциации могут быть присвоены имена ролей, исполняемые объектами классов, когда они связаны экземпляром данной ассоциации. Стрелка показывает направление передачи информации (например, посылки сообщения) от одного объекта к другому.
Например, предположим, что компания нанимает n служащих. Ограничения кратности (множественность — multiplicity) «1» и «n» означают, что объекты Person в данный момент времени могут быть наняты только одним объектом Company и Person не могут быть безработными. Объект Company может посылать сообщения объектам Person, но не наоборот.
Рисунок – Использование символов множественности на диаграмме классов
Недооцененная кратность ограничивает гибкость приложения, например, некоторые персональные менеджеры не позволяют охранять несколько телефонов для одного человека.
Частным случаем ассоциации является ассоциация-класс (Association Class), которая обладает как свойствами класса, так и свойствами ассоциации. Ассоциация-класс – это место, где хранятся относящиеся к ассоциации атрибуты и операции.
Имена полюсов ассоциаций являются обязательными, когда класс имеет ассоциацию с самим собой (рефлексивная ассоциация), которая означает, что объекты данного класса имеют связи с другими объектами этого же класса.
Каждый объект Directory может иметь связь с объектами Directory, выступающими в роли subdirectory, число которых может меняться от 0 до n. C другой стороны у подкаталога родитель может быть только 1 или не иметься вообще. Кроме того, Directory может иметь связь с произвольным числом объектов File.
Рисунок – Пример использования рефлексивной ассоциации на диаграмме классов
Ввиду широкого распространения ассоциаций типа многие-ко-многим в UML предусмотрены специальные классы ассоциации. Подобно обычным классам, классы ассоциаций могут иметь атрибуты и операции, являющиеся принадлежностью связи, т.е., такие, которые не могут быть приписаны ни к одному из объектов-участников ассоциации. На рис. атрибут accessPermission (разрешение доступа) класса AccessibleBy (Доступно) относится и к файлу и пользователю одновременно и не может быть прикреплен только к одному из них без потери информации.
Рисунок – Класс-ассоциация
Конкретизированные формы ассоциации (association):
§ зависимость (dependency) – однонаправленная связь, показывающая, что один класс зависит от определений, сделанных в другом. Такая связь имеет место, например, если один класс использует другой в качестве параметра операции. При генерации кода к исходному классу атрибуты целевого класса добавлены не будет, но в код будет добавлен оператор типа «#include».
§ агрегация (aggregation) – частный случай ассоциации, который выражает отношение целое-часть (например, автомобиль-двигатель). Агрегация является транзитивной: если А является частью В, а В – частью С, то А — часть С. Агрегация изображается с помощью ромба, который ставится около полюса, являющегося агрегатом. Жизненный цикл объекта–части совпадает с ЖЦ объекта-целого. Более сильная разновидность агрегации – это связь типа «композиция» (объединение – composition), которая накладывает на ассоциацию два дополнительных ограничения:
§ (i) составляющая часть может принадлежать не более чем одному агрегату;
§ (ii) составляющая часть получает срок жизни агрегата. Композиция обозначается закрашенным ромбом. Таким образом, агрегация – это способ встраивания нескольких объектов в один объект-контейнер и использование встраиваемых объектов для реализации методов контейнера.
Код:
Обобщение (generalization) – показывает связи наследования между двумя классами (суперкласс-подкласс). При этом, подклассы наследуют все возможности своих надклассов (атрибуты, операции, отношения, ограничения) – inheritance. Обобщение часто используется совместно с абстрактными классами, которые могут иметь абстрактные атрибуты и операции. Абстрактные классы не создают экземпляров. Подкласс абстрактного класса реализует абстрактные операции, добавляя новые возможности, или переопределяя их.
Рисунок – Наследование на диаграмме классов
Поскольку обобщение транзитивно, экземпляр подкласса одновременно является экземпляром всех его предков, т.е. обладает значениями всех атрибутов всех классов-предков и может вызывать любую операцию, указанную у любого из его предков. Не следует создавать слишком глубокую иерархию классов, поскольку это затруднит восприятие модели – иерархия с количеством уровней не превышающих 3, как правило, всегда приемлема; 5-6 уровней иерархии может быть как приемлемо, так и нет в зависимости от особенностей проектируемой системы.
Кроме того, обобщение следует использовать только там, где по крайней мере один из подклассов обладает атрибутами, операциями или ассоциациями, неприменимыми к суперклассу. Например, не следует использовать обобщение, когда существуют подклассы одинаковые по своей сигнатуре и поведению с суперклассом (масть карты Таким образом, ассоциации типа «обобщение» позволяют обеспечить полиморфизм:
добавляя новый подкласс автоматически наследуется поведение суперкласса и, более того, новый подкласс не нарушает работу существующего класса.
Следует отметить, что некоторыми языками программирования поддерживается множественное наследование, которое позволяет классу наследовать составляющие от нескольких классов одновременно.
На рисунке показано дерево классов, в котором SubClass является подклассом (дочерним классом) по отношению к двум суперклассам (базовым классам) – SuperClass1 и SuperClass22. Object1 и Object2 – экземпляры класса SubClass, наследующие атрибуты класса SubClass (x и y). В свою очередь SubClass наследует атрибуты z и w классов SuperClass1 и SuperClass2, переопределяет3 атрибут x и добавляет атрибут y.
Таким образом, при вызове, например Object1.x или Object2.x будет использоваться атрибут SubClass.x, находящийся на один уровень выше в иерархии наследования; при вызове Object1.z или Object2.z будет использоваться атрибут SuperClass1.z, поскольку класс SuperClass1 находится левее в дереве по сравнению с SuperClass2, то SubClass будет наследовать атрибуты SuperClass1 в первую очередь. Таким образом, при использовании множественного наследования следует иметь в виду, что порядок классов, перечисленных в строке заголовка инструкции class наследующего класса, определяет порядок наследования атрибутов.
Рисунок – Множественное наследование
Рисунок – Листинг кода, демонстрирующий множественное наследование
Множественное наследование стараются избегать или путем реструктурирования модели на этапе проектирования или с помощью механизма реализации «делегирование».
Видимость атрибута указывает на возможность его использования другими классами. Один класс может «видеть» другой, если тот находится в области действия первого и между ними существует явное или неявное отношение. В языке UML определены три уровня видимости:
§ public (общий) – любой внешний класс, который «видит» данный, может пользоваться его общими свойствами. Обозначаются знаком «+» перед именем атрибута или операции;
§ protected (защищенный) – только класс или потомок данного класса может пользоваться его защищенными свойствами. Обозначаются знаком «#»;
§ private (закрытый) – только данный класс может пользоваться закрытыми атрибутами. Обозначаются символом «-»;
§ package or implementation (пакетный) – атрибут является общим в пределах пакета в котором расположен класс. Обозначаются символом «~».
При определении видимости для той или иной составляющей необходимо руководствоваться следующими соображениями:
1. возможности класса определяются в первую очередь его открытыми составляющими, закрытые, защищенные и пакетные вводятся для удобства реализации;
2. открытые методы по определению используются множеством классов, поэтому изменение их сигнатуры (количества и типа аргументов, типа возвращаемого значения) может разрушить согласованность всей модели. Поскольку защищенные, закрытые и пакетные методы заведомо используются меньшим числом классов, их изменения обходятся дешевле.
Еще одной важной характеристикой атрибутов и операций классов является область действия (scope), которая определяет к чему относится данная составляющая: к объекту или к классу. У объектов есть собственные копии атрибутов, принимающих разные значения и операций. Область действия этих атрибутов и операций instance (экземпляр) – у каждого экземпляра класса есть собственное значение данного свойства. Однако иногда нужно определить атрибуты, которые имеют единственное, общее для всех объектов класса значение. Аналогично нужны операции, не относящиеся ни к одному конкретному экземпляру класса (например, операция создания объекта — конструктор). Такие атрибуты и операции имеют область действия classifier (классификатор) – все экземпляры совместно используют общее значение данного свойства (выделяется на диаграммах подчеркиванием).
В этом случае оно называется статическим.
Для группировки классов, обладающих общностью, применяют пакеты, которые являются средством организации модели в процессе разработки, повышения ее управляемости и читаемости. Диаграмма пакетов – диаграмма, содержащая пакеты классов и зависимости между ними. Кроме того, пакеты используются для представления подсистем (модулей) системы в процессе проектирования. Подсистема, включающая совокупность классов, реализует набор операций, которые определены в ее интерфейсе.
Рисунок – Диаграмма пакетов
Элемент может принадлежать пакету, в котором он определен или на него можно ссылаться из других пакетов. Если один элемент пакета зависит от элемента другого пакета, то эту зависимость можно отобразить с помощью связи.
Рисунок – Сопоставление пакетов проектным слоям
Каждый пакет представляет пространство имен (namespace), следовательно каждый класс внутри собственного пакета должен иметь уникальное имя. Чтобы отличить один класс от другого, можно использовать полностью определенное имя (fully qualified name), то есть имя, которое указывает на структуру, владеющую пакетом. В языке UML в именах пакетов используются двойные двоеточия, поэтому классы дат могут иметь имена System::Date.
Можно показывать только имя пакета или имя вместе с его содержимым.
Согласно концепции UML классы в пакетах могут быть открытыми (public) или закрытыми (private). Открытые классы представляют часть интерфейса пакета и могут быть использованы классами из других пакетов; закрытые классы недоступны извне. Можно сделать это, присвоив всем классам модификатор видимости private (закрытый), так чтобы они были доступны только классам данного пакета, а также создав дополнительные открытые классы для внешнего использования. Эти дополнительные классы, называемые Facades (Фасады), делегируют открытые операции другим классам пакета.