Основы программирования

Основы объектно-ориентированного представления программных систем
Индекс материала
Основы объектно-ориентированного представления программных систем
Инкапсуляция
Модульность
Иерархическая организация
Объекты
Виды отношений между объектами
Видимость объектов
Общая характеристика классов
Виды отношений между классами
Наследование
Полиморфизм
Агрегация
Зависимость
Базис языка визуального моделирования
Предметы в UML
Отношения в UML
Диаграммы в UML
Механизмы расширения в UML
Статические модели объектно-ориентированных программных систем
Операции
Отношения в диаграммах классов
Деревья наследования
Динамические модели объектно-ориентированных программных систем
Действия в состояниях
Вложенные состояния
Диаграммы взаимодействия
Диаграммы последовательности
Актеры и элементы Use Case
Работа с элементами Use Case
Подпотоки
Пример диаграммы Use Case
Построение модели требований
Определение элементов Use Case
Уточнение модели требований
Кооперации и паттерны
Паттерн Наблюдатель
Паттерн Компоновщик
Паттерн Команда
Бизнес-модели
Все страницы

Основы объектно-ориентированного представления программных систем

 

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

Принципы объектно-ориентированного представления программных систем

 

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

В основе алгоритмической декомпозиции лежит разбиение по действиям — алгоритмам. Эта схема представления применяется в обычных ПС.

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

Объектно-ориентированное представление ПС основывается на принципах абстрагирования, инкапсуляции, модульности и иерархической организации. Каждый из этих принципов не нов, но их совместное применение рассчитано на проведение объектно-ориентированной декомпозиции. Это определяет модификацию их содержания и механизмов взаимодействия друг с другом. Обсудим данные принципы [22], [32], [41], [59], [64], [66].

Абстрагирование

 

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

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

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

Пример: физический объект — датчик скорости, устанавливаемый на борту летательного аппарата (ЛА). Создадим его абстракцию. Для этого сформулируем обязанности датчика:

q       знать проекцию скорости ЛА в заданном направлении;

q       показывать текущую скорость;

q       подвергаться настройке.

Теперь опишем абстракцию датчика. Описание сформулируем как спецификацию класса на языке Ada 95 [4]:

Package Класс_ДатчикСкорости is

subtype Скорость is Float range ...

subtype Направление is Natural range ...

type ДатчикСкорости is tagged private;

function НовыйДатчик(нокер: Направление)

return ДатчикСкорости:

function ТекущаяСкорость (the: ДатчикСкорости)

return Скорость;

procedure Настраивать(the: in out ДатчикСкорости;

ДействитСкорость: Скорость);

private — закрытая часть спецификации

-- полное описание типа ДатчикСкорости

end Класс_ДатчикСкорости;

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

ДатчикПродольнойСкорости : ДатчикСкорости;

ДатчикПоперечнойСкорости : ДатчикСкорости;

ДатчикНормальнойСкорости : ДатчикСкорости;

Инкапсуляция

 

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

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

Пример: физический объект регулятор скорости.

Обязанности регулятора:

q       включаться;

q       выключаться;

q       увеличивать скорость;

q       уменьшать скорость;

q       отображать свое состояние.

Спецификация класса Регулятор скорости примет вид

with Кяасс_ДатчикСкорости. Класс_Порт;

use Класс_ДатчикСкорости. Класс_Порт;

Package Класс_РегуляторСкорости is

type Режим is (Увеличение, Уменьшение);

subtype Размещение is Natural range ...

type РегуляторСкорости is tagged private;

function НовРегуляторСкорости (номер: Размещение;

напр: Направление; порт; Порт)

return РегуляторСкорости;

procedure Включить(the: in out РегуляторСкорости);

procedure Выключить(1пе: in out РегуляторСкорости);

procedure УвеличитьСкорость(1г1е: in out

РегуляторСкорости);

procedure УменьшитьСкорость(the: in out

РегуляторСкорости);

Function OnpocCocтояния(the: РегуляторСкорости)

eturn Режим;

private

type укз_наПорт is access all Порт;

type РегуляторСкорости is tagged record

Номер; Размещение;

Состояние: Режим;

Управление: укз_наПорт;

end record;

end Класс_РегуляторСкорости;

Здесь вспомогательный тип Режим используется для задания основного типа класса, класс ДатчикСкорости обеспечивает класс регулятора описанием вспомогательного типа Направление, класс Порт фиксирует абстракцию порта, через который посылаются сообщения для регулятора. Три свойства: Номер, Состояние, Управление — формулируют инкапсулируемое представление основного типа класса РегуляторСкорости. При попытке клиента получить доступ к этим свойствам фиксируется семантическая ошибка.

Полное инкапсулированное представление класса РегуляторСкорости включает описание реализаций его методов — оно содержится в теле класса. Описание тела для краткости здесь опущено.


Модульность

 

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

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

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

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

В Ada 95 мощным средством обеспечения модульности является пакет.

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

1. Присоединение с помощью указателей контекста:

with Класс_УгловСтабил, Класс_ДвиженЦентраМасс;

use Класс_УгловСтабил, Класс_ДвиженЦентраМасс;

Package Класс_УпрПолетом is

2. Встраивание программ управления непосредственно в объединенный модуль:

Package Класс_УпрПолетом is

type УгловСтабил is tagged private;

type ДвиженЦентраМасс is tagged private;

-------------------------


Иерархическая организация

 

Мы рассмотрели три механизма для борьбы со сложностью:

q       абстракцию (она упрощает представление физического объекта);

q       инкапсуляцию (закрывает детали внутреннего представления абстракций);

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

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

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

Двумя важными инструментами иерархической организации в объектно-ориентированных системах являются:

q       структура из классов is a»-иерархия);

q       структура из объектов part of»-иерархия).

Чаще всего «is а»-иерархическая структура строится с помощью наследования. Наследование определяет отношение между классами, где класс разделяет структуру или поведение, определенные в одном другом (единичное наследование) или в нескольких других (множественное наследование) классах.

Пример: положим, что программа управления полетом 2-й ступени ракеты-носителя в основном похожа на программу управления полетом 1-й ступени, но все же отличается от нее. Определим класс управления полетом 2-й ступени, который инкапсулирует ее специализированное поведение:

with Класс_УпрПолетом1; use Класс_УпрПолетом1;

Package Класс_УпрПолетом2 is

type Траектория is (Гибкая. Свободная);

type УпрПолетом2 is new УпрПолетом1 with private;

procedure Установиться: in out УпрПолетом2:

тип: Траектория; ориентация : Углы;

параметры: Координаты_Скорость; команды: График);

procedure УсловияОтделенияЗступени (the: УпрПолетом2;

критерий:КритерийОтделения);

function ПрогнозОтделенияЗступени (the: УпрПолетом2)

return БортовоеВремя;

function ИсполнениеКоманд(the: УпрПолетом2)

return Boolean;

private

type УпрПолетом2 is new УпрПолетом1

with record

типТраектории: Траектория; доОтделения: БортовоеВремя;

выполнениеКоманд: Boolean;

end record;

end Класс_УпрПолетом2;

Видим, что класс УпрПолетом2 — это «is а»-разновидность класса УпрПолетом1, который называется родительским классом или суперклассом.

В класс УпрПолетом2 добавлены:

q       авспомогательный тип Траектория;

q       три новых свойства (типТраектории, доОтделения, выполнениеКоманд);

q       три новых метода (УсловияОтделенияЗступени, ПрогнозОтделенияЗступени, ИсполнениеКоманд).

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

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

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

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

1)      агрегация обеспечивает физическую группировку логически связанной структуры;

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

Приведем пример класса ИзмерительСУ (измеритель системы управления ЛА):

with Класс_НастройкаДатчиков. Класс_Датчик;

use Класс_НастройкаДатчиков, Класс_Датчик;

Package Класс_ИзмерительСУ is

type ИзмерительСУ is tagged private;

-- описание методов

private

type укз_наДатчик is access all Датчик'Class;

type ИзмерительСУ is record

датчики: array(1..30) of укз_наДатчик;

процедураНастройки: НастройкаДатчиков;

end record;

end Класс_ИзмерительСУ;

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

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


Объекты

 

Рассмотрим более пристально объекты — конкретные сущности, которые существуют во времени и пространстве.

Общая характеристика объектов

Объект — это конкретное представление абстракции. Объект обладает индивидуальностью, состоянием и поведением. Структура и поведение подобных объектов определены в их общем классе. Термины «экземпляр класса» и «объект» взаимозаменяемы. На рис. 9.1 приведен пример объекта по имени Стул, имеющего определенный набор свойств и операций.

Индивидуальность — это характеристика объекта, которая отличает его от всех других объектов.

Состояние объекта характеризуется перечнем всех свойств объекта и текущими значениями каждого из этих свойств (рис. 9.1).

 

Рис. 9.1. Представление объекта с именем Стул

 

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

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

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

1)      модификатор (изменяет состояние объекта);

2)      селектор (дает доступ к состоянию, но не изменяет его);

3)      итератор (доступ к содержанию объекта по частям, в строго определенном порядке);

4)      конструктор (создает объект и инициализирует его состояние);

5)      деструктор (разрушает объект и освобождает занимаемую им память). Примеры операций приведены в табл. 9.1.

Таблица 9.1. Разновидности операций

Вид операции

Пример операции

Модификатор Селектор Итератор Конструктор Деструктор

Пополнеть (кг)

КакойВес ( ) : integer

ПоказатьАссортиментТоваров ( ) : string

СоздатьРобот (параметры)

УничтожитьРобот ( )

 

В чистых объектно-ориентированных языках программирования операции могут объявляться только как методы — элементы классов, экземплярами которых являются объекты. Гибридные языки (C++, Ada 95) позволяют писать операции как свободные подпрограммы (вне классов). Соответствующие примеры показаны на рис. 9.2.

 

Рис. 9.2. Методы и свободные подпрограммы

 

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

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

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

 

Рис. 9.3. Пространство поведения объекта

 

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

 

Рис.9.4. Активные и пассивные объекты

 

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


Виды отношений между объектами

 

В поле зрения разработчика ПО находятся не объекты-одиночки, а взаимодействующие объекты, ведь именно взаимодействие объектов реализует поведение системы. У Г. Буча есть отличная цитата из Галла: «Самолет — это набор элементов, каждый из которых по своей природе стремится упасть на землю, но ценой совместных непрерывных усилий преодолевает эту тенденцию» [22]. Отношения между парой объектов основываются на взаимной информации о разрешенных операциях и ожидаемом поведении. Особо интересны два вида отношений между объектами: связи и агрегация.

Связи

 

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

q       объект-клиент вызывает операции объекта-поставщика;

q       один объект перемещает данные к другому объекту.

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

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

 

Рис. 9.5. Связи между объектами

 

Как участник связи объект может играть одну из трех ролей:

q       актер — объект, который может воздействовать на другие объекты, но никогда не подвержен воздействию других объектов;

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

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

На рис. 9.5 Том — это актер, Мери, Колонки — серверы, Музыкальный центр — агент.

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

Запишем абстракцию графика разворота:

with Класс_ДатчикУглаТангажа;

use Класс_ДатчикУглаТангажа;

Package Класс_ГрафикРазворота is

subtype Секунда is Natural range ...

type ГрафикРазворота is tagged private;

procedure Очистить (the: in out ГрафикРазворота);

procedure Связать (the: In out ГрафикРазворота;

teta: Угол: si: Секунда: s2: Секунда);

function УголНаМомент (the: ГрафикРазворота;

s: Секунда) return Угол;

private

end Класс_ГрафикРазворота;

 

Рис. 9.6. График разворота первой ступени ракеты

 

Для решения задачи надо обеспечить сотрудничество трех объектов: экземпляра класса ГрафикРазворота, РегулятораУгла и КонтроллераУгла.

Описание класса КонтроллерУгла может иметь следующий вид:

with Класс_ГрафикРазворота. Класс_РегуляторУгла;

use Класс_ГрафикРазворота. Класс_РегуляторУгла;

Package Класс_КонтроллерУгла is

type укз_наГрафик is access all ГрафикРазворота;

type КонтроллерУгла is tagged private;

procedure Обрабатывать (the: in out КонтроллерУгла;

уг: укз_наГрафик);

function Запланировано (the: КонтроллерУгла;

уг: укз_наГрафик) return Секунда;

private

type КонтроллерУгла is tagged record

регулятор: РегуляторУгла := НовРегуляторУгла

(1.1.10);

end Класс_КонтроллерУгла:

ПРИМЕЧАНИЕ

Операция Запланировано позволяет клиентам запросить у экземпляра Контроллера-Угла время обработки следующего графика.

 

И наконец, описание класса РегуляторУгла представим в следующей форме:

with Класс_ДатчикУгла. Класс_Порт;

use Класс_ДатчикУгла. Класс_Порт;

Package Класс_РегуляторУгла is

type Режим is (Увеличение. Уменьшение);

subtype Размещение is Natural range ...

type РегуляторУгла is tagged private;

function НовРегуляторУгла (номер: Размещение;

напр: Направление: порт: Порт)

return РегуляторУгла;

procedure Включить(the: in out РегуляторУгла);

procedure Выключить(the: in out РегуляторУгла);

procedure УвеличитьУгол(№е: in out

РегуляторУгла);

procedure УменьшитьУгол(the: in out

РегуляторУгла);

function ОпросСостояния(the: РегуляторУгла)

return Режим:

private

type укз_наПорт is access all Порт;

type РегуляторУгла is tagged record

Номер: Размещение;

Состояние: Режим;

Управление: укз_наПорт;

end record;

end Класс_РегуляторУгла;

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

РабочийГрафик: aliased ГрафикРазворота;

РабочийКонтроллер: aliased Контроллеругла;

Далее мы должны определить конкретные параметры графика разворота

Связать (РабочийГрафик. 30. 60. 90);

а затем предложить объекту-контроллеру выполнить этот график:

Обрабатывать (РабочийКонтроллер. РабочийГрафикАссеss);

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


Видимость объектов

 

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

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

Различают четыре формы видимости между объектами.

1. Объект-поставщик (сервер) глобален для клиента.

2. Объект-поставщик (сервер) является параметром операции клиента.

3. Объект-поставщик (сервер) является частью объекта-клиента.

4. Объект-поставщик (сервер) является локально объявленным объектом в операции клиента.

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

Агрегация

 

Связи обозначают равноправные (клиент-серверные) отношения между объектами. Агрегация обозначает отношения объектов в иерархии «целое/часть». Агрегация обеспечивает возможность перемещения от целого (агрегата) к его частям (свойствам).

В примере из подраздела «Связи» объект РабочийКонтроллер имеет свойство регулятор, чьим классом является РегуляторУгла. Поэтому объект РабочийКонтроллер является агрегатом (целым), а экземпляр РегулятораУгла — одной из его частей. Из РабочегоКонтроллера всегда можно попасть в его регулятор. Обратный же переход (из части в целое) обеспечивается не всегда.

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

 

Рис. 9.7. Физическое включение частей в агрегат

 

На рис. 9.8 приведен пример нефизического включения частей (Студента, Преподавателя) в агрегат Вуз. Очевидно, что Студент и Преподаватель являются элементами Вуза, но они не входят в него физически. В этом случае говорят, что части включены в агрегат по ссылке.

 

Рис. 9.8. Нефизическое включение частей в агрегат

 

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

При выборе вида отношения должны учитываться следующие факторы:

q       связи обеспечивают низкое сцепление между объектами;

q       агрегация инкапсулирует части как секреты целого.

Классы

 

Понятия объекта и класса тесно связаны. Тем не менее существует важное различие между этими понятиями. Класс — это абстракция существенных характеристик объекта.


Общая характеристика классов

Класс — описание множества объектов, которые разделяют одинаковые свойства, операции, отношения и семантику (смысл). Любой объект — просто экземпляр класса.

Как показано на рис. 9.9, различают внутреннее представление класса (реализацию) и внешнее представление класса (интерфейс).

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

 

Рис. 9.9. Структуре представления класса

 

Интерфейс может быть разделен на 3 части:

1)      публичную (public), объявления которой доступны всем клиентам;

2)      защищенную (protected), объявления которой доступны только самому классу, его подклассам и друзьям;

3)      приватную (private), объявления которой доступны только самому классу и его друзьям.

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

ПРИМЕЧАНИЕ

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

 

Реализация класса описывает секреты поведения класса. Она включает реализации всех операций, определенных в интерфейсе класса.


Виды отношений между классами

 

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

Всего существует четыре основных вида отношений между классами:

q       ассоциация (фиксирует структурные отношения — связи между экземплярами классов);

q       зависимость (отображает влияние одного класса на другой класс);

q       обобщение-специализация («is а»-отношение);

q       целое-часть («part of»-отношение).

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

1) ассоциация;

2) наследование;

3) агрегация;

4) зависимость;

5) конкретизация;

6) метакласс;

7) реализация.

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

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

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

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

Конкретизация выражает другую разновидность отношения обобщение-специализация. Применяется в таких языках, как Ada 95, C++, Эйфель.

Отношения метаклассов поддерживаются в языках SmallTalk и CLOS. Метакласс — это класс классов, понятие, позволяющее обращаться с классами как с объектами.

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

Ассоциации классов

 

Ассоциация обозначает семантическое соединение классов.

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

 

Рис. 9.10. Ассоциация

 

Отношение ассоциации между классами изображено на рис. 9.10. Очевидно, что ассоциация предполагает двухсторонние отношения:

q       для данного экземпляра Книги выделяется экземпляр Библиотеки, обеспечивающий ее хранение;

q       для данного экземпляра Библиотеки выделяются все хранимые Книги.

Здесь показана ассоциация один-ко-многим. Каждый экземпляр Книги имеет указатель на экземпляр Библиотеки. Каждый экземпляр Библиотеки имеет набор указателей на несколько экземпляров Книги.

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

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

q       один-к-одному;

q       один-ко-многим;

q       многие-ко-многим.

Примеры ассоциаций с различными типами мощности приведены на рис. 9.11, они имеют следующий смысл:

q       у европейской жены один муж, а у европейского мужа одна жена;

q       у восточной жены один муж, а у восточного мужа сколько угодно жен;

q       у заказа один клиент, а у клиента сколько угодно заказов;

q       человек может посещать сколько угодно зданий, а в здании может находиться сколько угодно людей.

 



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

 

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

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

Пример: дана система для записи параметров полета в «черный ящик», установленный в самолете. Организуем систему в виде иерархии классов, построенной на базе наследования. Абстракция «верхнего» класса иерархии имеет вид

with ...;...

use ...; ...

Package Класс_ПараметрыПолета is

type ПараметрыПолета is tagged private;

function Инициировать return ПараметрыПолета;

procedure Записывать (the: in out ПараметрыПолета);

function ТекущВремя (the: ПараметрыПолета)

return БортовоеВремя;

private

type ПараметрыПолета is tagged record

Имя: integer;

ОтметкаВремени: БортовоеВремя;

end record;

end Класс_ПараметрыПолета;

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

with Класс_ПараметрыПолета; ...

use Класс_ПараметрыПолета; ...

Package Класс_Кабина is

type Кабина is new ПараметрыПолета with private;

function Инициировать (Д:Давление; К:Кислород;

Т:Температура) return Кабина;

procedure Записывать (the: in out Кабина);

function ПерепадДавления (the: Кабина) return Давление;

private

type Кабина is new ПараметрыПолета

with record

параметр1: Давление;

параметр2: Кислород;

параметр3: Температура

end record;

end Класс_Кабина;

Этот класс наследует структуру и поведение класса ПараметрыПолета, но наращивает его структуру (вводит три новых элемента данных), переопределяет его поведение (процедура Записывать) и дополняет его поведение (функция ПерепадДавления).

Иерархическая структура классов системы для записи параметров полета, находящихся в отношении наследования, показана на рис. 9.12.

 

Рис. 9.12. Иерархия простого наследования

 

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


Полиморфизм

 

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

Рассмотрим различные реализации процедуры Записывать. Для класса ПараметрыПолета реализация имеет вид

procedure Записывать (the: in out ПараметрыПолета) is

begin

-- записывать имя параметра

-- записывать отметку времени

end Записывать;

В классе Кабина предусмотрена другая реализация процедуры:

procedure Записывать (the: in out Кабина) is

begin

Записывать (ПараметрыПолета (the)); -- вызов метода

-- суперкласса

-- записывать значение давления

-- записывать процентное содержание кислорода

-- записывать значение температуры

end Записывать;

Предположим, что мы имеем по экземпляру каждого из этих двух классов:

Вполете: ПараметрыПолета:= Инициировать;

Вкабине: Кабина:= Инициировать (768. 21. 20);

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

procedure СохранятьНовДанные (d: in out

ПараметрыПолета'class; t: БортовоеВремя) is

begin

if ТекущВремя(d) >= t then

Записывать (d): -- диспетчирование с помощью тега

end if;

end СохранятьНовДанные;

Что случится при выполнении следующих операторов?

q       СохранятьНовДанные (Вполете, БортовоеВремя (60));

q       СохранятьНовДанные (Вкабине, БортовоеВремя (120));

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


Агрегация

 

Отношения агрегации между классами аналогичны отношениям агрегации между объектами.

Повторим пример с описанием класса КонтроллерУгла:

with Класс_ГрафикРазворота. Класс_РегуляторУгла;

use Класс_ГрафикРазворота, Класс_РегуляторУгла;

Package Класс_КонтроллерУгла is

type укз_наГрафик is access all ГрафикРазворота;

type Контроллеругла is tagged private:

procedure Обрабатывать (the: in out Контроллеругла;

yr: укз_наГрафик);

function Запланировано (the: КонтроллерУгла;

уr: укз_наГрафик) return Секунда;

private

type КонтроллерУгла is tagged record

регулятор: РегуляторУгла;

end Класс_КонтроллерУгла;

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

Графическая иллюстрация отношения агрегации по величине (композиции) представлена на рис. 9.13.

 

Рис. 9.13. Отношение агрегации по величине (композиция)

 

Возможен косвенный тип агрегации — включением по ссылке. Если мы запишем в приватной части класса КонтроллерУгла:

private

type укз_наРегуляторУгла is access all РегуляторУгла;

type КонтроллерУгла is tagged record

регулятор: укз_наРегуляторУгла;

end Класс_КонтроллерУгла;

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

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

Еще два примера агрегации по ссылке и по величине (композиции) приведены на рис. 9.14. Здесь показаны класс-агрегат Дом и класс-агрегат Окно, причем указаны роли и множественность частей агрегата (соответствующие пометки имеют линии отношений).

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

роль : имяКласса.

 

Рис. 9.14. Агрегация классов

 

 

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

 

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

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


Зависимость

 

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

Наиболее часто зависимости показывают, что один класс использует другой класс как аргумент в сигнатуре своей операции. В предыдущем примере (на языке Ada 95) класс ГрафикРазворота появляется как аргумент в методах Обрабатывать и Запланировано класса КонтроллерУгла. Поэтому, как показано на рис. 9.16, КонтроллерУгла зависит от класса ГрафикРазворота.

 

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

 

Конкретизация

 

Г. Буч определяет конкретизацию как процесс наполнения шаблона (родового или параметризованного класса). Целью является получение класса, от которого возможно создание экземпляров [22].

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

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

Пример: представим родовой (параметризированный) класс Очередь:

generic

type Элемент is private;

package Класс_Очередь is

type Очередь is limited tagged private;

procedure Добавить (В_0чередь: in out Очередь;

элт: Элемент );

private

end Класс_0чередь;

У этого класса один формальный родовой параметр — тип Элемент. Вместо этого параметра можно подставить почти любой тип данных.

Произведем настройку, то есть объявим два конкретизированных класса — Оче-редьЦелыхЭлементов и ОчередьЛилипутов:

package Класс_ОчередьЦелыхЭлементов is new Класс_0чередь

(Элемент => Integer);

package Класс_ОчередьЛилипутов is new Класс_0чередь

(Элемент => Лилипут);

В первом случае мы настраивали класс на конкретный тип Integer (фактический родовой параметр), во втором случае — на конкретный тип Лилипут.

Классы ОчередьЦелыхЭлементов и ОчередьЛилипутов можно использовать как обычные классы. Они содержат все средства родового класса, но только эти средства настроены на использование конкретного типа, заданного при конкретизации.

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

 

Рис. 9.17. Отношения конкретизации родового класса

 

Контрольные вопросы

 

1.           В чем отличие алгоритмической декомпозиции от объектно-ориентированной декомпозиции сложной системы?

2.           В чем особенность объектно-ориентированного абстрагирования?

3.           В чем особенность объектно-ориентированной инкапсуляции?

4.           Каковы средства обеспечения объектно-ориентированной модульности?

5.           Каковы особенности объектно-ориентированной иерархии? Какие разновидности этой иерархии вы знаете?

6.           Дайте общую характеристику объектов.

7.           Что такое состояние объекта?

8.           Что такое поведение объекта?

9.           Какие виды операций вы знаете?

10.       Что такое протокол объекта?

11.       Что такое обязанности объекта?

12.       Чем отличаются активные объекты от пассивных объектов?

13.       Что такое роли объектов?

14.       Чем отличается объект от класса?

15.       Охарактеризуйте связи между объектами.

16.       Охарактеризуйте роли объектов в связях.

17.       Какие формы видимости между объектами вы знаете?

18.       Охарактеризуйте отношение агрегации между объектами. Какие разновидности агрегации вы знаете?

19.       Дайте общую характеристику класса.

20.       Поясните внутреннее и внешнее представление класса.

21.       Какие вы знаете секции в интерфейсной части класса?

22.       Какие виды отношений между классами вы знаете?

23.       Поясните ассоциации между классами.

24.       Поясните наследование классов.

25.       Поясните понятие полиморфизма.

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

27.       Объясните нетрадиционные формы представления агрегации.

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

29.       Поясните отношение конкретизации между классами.


ГЛАВА 10. Базис языка визуального моделирования

 

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

В настоящее время различают три поколения языков визуального моделирования. И если первое поколение образовали 10 языков, то численность второго поколения уже превысила 50 языков. Среди наиболее популярных языков 2-го поколения можно выделить: язык Буча (G. Booch), язык Рамбо (J. Rumbaugh), язык Джекобсона (I. Jacobson), язык Коада-Йордона (Coad-Yourdon), язык Шлеера-Меллора (Shlaer-Mellor) и т. д [41], [64], [69]. Каждый язык вводил свои выразительные средства, ориентировался на собственный синтаксис и семантику, иными словами — претендовал на роль единственного и неповторимого языка. В результате разработчики (и пользователи этих языков) перестали понимать друг друга. Возникла острая необходимость унификации языков.

Идея унификации привела к появлению языков 3-го поколения. В качестве стандартного языка третьего поколения был принят Unified Modeling Language (UML), создававшийся в 1994-1997 годах (основные разработчики — три «amigos» Г. Буч, Дж. Рамбо, И. Джекобсон). В настоящее время разработана версия UML 1.4, которая описывается в данном учебнике [53]. Данная глава посвящена определению базовых понятий языка UML.

Унифицированный язык моделирования

 

UML — стандартный язык для написания моделей анализа, проектирования и реализации объектно-ориентированных программных систем [23], [53], [67]. UML может использоваться для визуализации, спецификации, конструирования и документирования результатов программных проектов. UML — это не визуальный язык программирования, но его модели прямо транслируются в текст на языках программирования (Java, C++, Visual Basic, Ada 95, Object Pascal) и даже в таблицы для реляционной БД.

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

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


Предметы в UML

 

В UML имеются четыре разновидности предметов:

q       структурные предметы;

q       предметы поведения;

q       группирующие предметы;

q       поясняющие предметы.

Эти предметы являются базовыми объектно-ориентированными строительными блоками. Они используются для написания моделей.

Структурные предметы являются существительными в UML-моделях. Они представляют статические части модели — понятийные или физические элементы. Перечислим восемь разновидностей структурных предметов.

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

 

Рис. 10.1. Классы

 

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

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

 

Рис. 10.3. Кооперации

 

4.      Актер — набор согласованных ролей, которые могут играть пользователи при взаимодействии с системой (ее элементами Use Case). Каждая роль требует от системы определенного поведения. Как показано на рис. 10.4, актер изображается как проволочный человечек с именем.

 

Рис. 10.4. Актеры

 

5.      Элемент Use Case (Прецедент) — описание последовательности действий (или нескольких последовательностей), выполняемых системой в интересах отдельного актера и производящих видимый для актера результат. В модели элемент Use Case применяется для структурирования предметов поведения. Элемент Use Case реализуется кооперацией. Как показано на рис. 10.5, элемент Use Case изображается как эллипс, в который вписывается его имя.

 

Рис. 10.5. Элементы Use Case

 

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

 

Рис. 10.6. Активные классы

 

7.      Компонент — физическая и заменяемая часть системы, которая соответствует набору интерфейсов и обеспечивает реализацию этого набора интерфейсов. В систему включаются как компоненты, являющиеся результатами процесса разработки (файлы исходного кода), так и различные разновидности используемых компонентов (СОМ+-компоненты, Java Beans). Обычно компонент — это физическая упаковка различных логических элементов (классов, интерфейсов и сотрудничеств). Как показано на рис. 10.7, компонент изображается как прямоугольник с вкладками, обычно включающий имя.

 

Рис. 10.7. Компоненты

 

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

 

Рис. 10.8. Узлы

 

Предметы поведения — динамические части UML-моделей. Они являются глаголами моделей, представлением поведения во времени и пространстве. Существует две основные разновидности предметов поведения.

1.      Взаимодействие — поведение, заключающее в себе набор сообщений, которыми обменивается набор объектов в конкретном контексте для достижения определенной цели. Взаимодействие может определять динамику как совокупности объектов, так и отдельной операции. Элементами взаимодействия являются сообщения, последовательность действий (поведение, вызываемое сообщением) и связи (соединения между объектами). Как показано на рис. 10.9, сообщение изображается в виде направленной линии с именем ее операции.

 

Рис. 10.9. Сообщения

 

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

 

Рис. 10.10. Состояния

 

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

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

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

 

Рис. 10.11. Пакеты

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

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

 

Рис. 10.12. Примечания


Отношения в UML

 

В UML имеются четыре разновидности отношений:

1) зависимость;

2) ассоциация;

3) обобщение;

4) реализация.

Эти отношения являются базовыми строительными блоками отношений. Они используются при написании моделей.

1.      Зависимость — семантическое отношение между двумя предметами, в котором изменение в одном предмете (независимом предмете) может влиять на семантику другого предмета (зависимого предмета). Как показано на рис. 10.13, зависимость изображается в виде пунктирной линии, возможно направленной на независимый предмет и иногда имеющей метку.

 

Рис. 10.13. Зависимости

 

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

 

Рис. 10.14. Ассоциации

 

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

 

Рис. 10.15. Обобщения

 

4.      Реализация — семантическое отношение между классификаторами, где один классификатор определяет контракт, который другой классификатор обязуется выполнять (к классификаторам относят классы, интерфейсы, компоненты, элементы Use Case, кооперации). Отношения реализации применяют в двух случаях: между интерфейсами и классами (или компонентами), реализующими их; между элементами Use Case и кооперациями, которые реализуют их. Как показано на рис. 10.16, реализация изображается как нечто среднее между обобщением и зависимостью.

 

Рис. 10.16. Реализации

 


Диаграммы в UML

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

1) диаграммы классов;

2) диаграммы объектов;

3) диаграммы Use Case (диаграммы прецедентов);

4) диаграммы последовательности;

5) диаграммы сотрудничества (кооперации);

6) диаграммы схем состояний;

7) диаграммы деятельности;

8) компонентные диаграммы;

9) диаграммы размещения (развертывания).

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

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

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

Диаграммы последовательности и диаграммы сотрудничества — это разновидности диаграмм взаимодействия.

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

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

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

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

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

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

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


Механизмы расширения в UML

 

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

q       ограничения;

q       теговые величины;

q       стереотипы.

Ограничение (constraint) расширяет семантику строительного UML-блока, позволяя добавить новые правила или модифицировать существующие. Ограничение показывают как текстовую строку, заключенную в фигурные скобки {}. Например, на рис. 10.17 введено простое ограничение на свойство сумма класса Сессия Банкомата — его значение должно быть кратно 20. Кроме того, здесь показано ограничение на два элемента (две ассоциации), оно располагается возле пунктирной линии, соединяющей элементы, и имеет следующий смысл — владельцем конкретного счета не может быть и организация, и персона.

 

Рис. 10.17. Ограничения

Теговая величина (tagged value) расширяет характеристики строительного UML-блока, позволяя создать новую информацию в спецификации конкретного элемента. Теговую величину показывают как строку в фигурных скобках {}. Строка имеет вид

имя теговой величины = значение.

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

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

 

Рис. 10.18. Расширение класса

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

Примеры элементов со стереотипами приведены на рис. 10.19. Стереотип «exception» говорит о том, что класс ПотеряЗначимости теперь рассматривается как специальный класс, которому, положим, разрешается только генерация и обработка сигналов исключений. Особые возможности метакласса получил класс ЭлементМодели. Кроме того, здесь показано применение стереотипа «call» к отношению зависимости (у него появился новый смысл).

 

Рис. 10.19. Стереотипы

 

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

Контрольные вопросы

 

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

2.      Назовите численность языков визуального моделирования 2-го поколения.

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

4.      Поясните назначение UML.

5.      Какие строительные блоки образуют словарь UML? Охарактеризуйте их.

6.      Какие разновидности предметов UML вы знаете? Их назначение?

7.      Перечислите известные вам разновидности структурных предметов UML.

8.      Перечислите известные вам разновидности предметов поведения UML.

9.      Перечислите известные вам группирующие предметы UML.

10.  Перечислите известные вам поясняющие предметы UML.

11.  Какие разновидности отношений предусмотрены в UML? Охарактеризуйте каждое отношение.

12.  Дайте характеристику диаграммы классов.

13.  Дайте характеристику диаграммы объектов.

14.  Охарактеризуйте диаграмму Use Case.

15.  Охарактеризуйте диаграммы взаимодействия.

16.  Дайте характеристику диаграммы последовательности.

17.  Дайте характеристику диаграммы сотрудничества.

18.  Охарактеризуйте диаграмму схем состояний.

19.  Охарактеризуйте диаграмму деятельности.

20.  Дайте характеристику компонентной диаграммы.

21.  Охарактеризуйте диаграмму размещения.

22.  Для чего служат механизмы расширения в UML?

23.  Поясните механизм ограничений в UML.

24.  Объясните механизм теговых величин в UML.

25.  В чем суть механизма стереотипов UML?

ГЛАВА 11. Статические модели объектно-ориентированных программных систем

 

Статические модели обеспечивают представление структуры систем в терминах базовых строительных блоков и отношений между ними. «Статичность» этих моделей состоит в том, что здесь не показывается динамика изменений системы во времени. Вместе с тем следует понимать, что эти модели несут в себе не только структурные описания, но и описания операций, реализующих заданное поведение системы. Основным средством для представления статических моделей являются диаграммы классов [8], [23], [53], [67]. Вершины диаграмм классов нагружены классами, а дуги (ребра) — отношениями между ними. Диаграммы используются:

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

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

Вершины в диаграммах классов

 

Итак, вершина в диаграмме классов — класс. Обозначение класса показано на рис. 11.1.

 

Рис. 11.1. Обозначение класса

 

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

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

 

Рис. 11.2. Свойства уровней класса и экземпляра

 

Свойства

 

Общий синтаксис представления свойства имеет вид

Видимость Имя [Множественность]: Тип = НачальнЗначение {Характеристики}

Рассмотрим видимость и характеристики свойств.

В языке UML определены три уровня видимости:

public

 

protected

 

private

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

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

Свойство (операция) может использоваться только самим классом, обозначается символом -

ПРИМЕЧАНИЕ

Если видимость не указана, считают, что свойство объявлено с публичной видимостью.

 

Определены три характеристики свойств:

changeable

addOnly

 

frozen

Нет ограничений на модификацию значения свойства

Для свойств с множественностью, большей единицы; дополнительные значения могут быть добавлены, но после создания значение не может удаляться или изменяться

После инициализации объекта значение свойства не изменяется

ПРИМЕЧАНИЕ

Если характеристика не указана, считают, что свойство объявлено с характеристикой changeable.

 

Примеры объявления свойств:

начало

+ начало

начало : Координаты

имяфамилия [0..1] : String

левыйУгол : Координаты=(0, 10)

сумма : Integer {frozen}

Только имя

Видимость и имя

Имя и тип

Имя, множественность, тип

Имя, тип, начальное значение

Имя и характеристика


Операции

 

Общий синтаксис представления операции имеет вид

Видимость Имя (Список Параметров): ВозвращаемыйТип {Характеристики}

Примеры объявления операций:

записать

+ записать

зарегистрировать) и: Имя, ф: Фамилия)

балансСчета ( ) : Integer

нагревать ( ) (guarded)

Только имя

Видимость и имя

Имя и параметры

Имя и возвращаемый тип

Имя и характеристика

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

Направление Имя : Тип = ЗначениеПоУмолчанию

Элемент Направление может принимать одно из следующих значений:

 

in

out

 

inout

Входной параметр, не может модифицироваться

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

Входной параметр, может модифицироваться

 

Допустимо применение следующих характеристик операций:

 

leaf

 

isQuery

sequential

 

guarded

 

 

 

 

concurrent

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

Выполнение операции не изменяет состояния объекта

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

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

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

Организация свойств и операций

 

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

 

Рис. 11.3. Стереотипы для характеристик класса

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

 

Иногда бывает необходимо ограничить количество экземпляров класса:

q       задать ноль экземпляров (в этом случае класс превращается в утилиту, которая предлагает свои свойства и операции);

q       задать один экземпляр (класс-singleton);

q       задать конкретное количество экземпляров;

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

Количество экземпляров класса называется его множественностью. Выражение множественности записывается в правом верхнем углу значка класса. Например, как показано на рис. 11.4, КонтроллерУглов — это класс-singleton, а для класса ДатчикУгла разрешены три экземпляра.

 

Рис. 11.4. Множественность

 

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


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

 

Отношения, используемые в диаграммах классов, показаны на рис. 11.5.

 

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

 

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

 

Рис. 11.6. Имена ассоциаций

 

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

 

Рис. 11.7. Роли

 

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

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

q       5 — точно пять;

q       * — неограниченное количество;

q       0..* — ноль или более;

q       1..* — один или более;

q       3..7 — определенный диапазон;

q       1..3, 7 — определенный диапазон или число.

 

Рис. 11. 8. Мощность

 

Достаточно часто возникает следующая проблема — как для объекта на одном конце ассоциации выделить набор объектов на противоположном конце? Например, рассмотрим взаимодействие между банком и клиентом — вкладчиком. Как показано на рис. 11.9, мы устанавливаем ассоциацию между классом Банк и классом Клиент. В контексте Банка мы имеем НомерСчета, который позволяет идентифицировать конкретного Клиента. В этом смысле НомерСчета является атрибутом ассоциации. Он не является характеристикой Клиента, так как Клиенту не обязательно знать служебные параметры его счета. Теперь для данного экземпляра Банка и данного значения НомераСчета можно выявить ноль или один экземпляр Клиента. В UML для решения этой проблемы вводится квалификатор — атрибут ассоциации, чьи значения выделяют набор объектов, связанных с объектом через ассоциацию. Квалификатор изображается маленьким прямоугольником, присоединенным к концу ассоциации. В прямоугольник вписывается свойство — атрибут ассоциации.

 

Рис. 11.9. Квалификация

 

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

 

Рис. 11.10. Видимость

 

На конце ассоциации можно задать три уровня видимости, добавляя символ видимости к имени роли:

q       по умолчанию для роли задается публичная видимость;

q       приватная видимость указывает, что объекты на данном конце недоступны любым объектам вне ассоциации;

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

В языке UML ассоциации могут иметь свойства. Как показано на рис, 11.11, такие возможности отображаются с помощью классов-ассоциаций. Эти классы присоединяются к линии ассоциации пунктирной линией и рассматриваются как классы со свойствами ассоциаций или как ассоциации со свойствами классов.

 

Рис. 11.11. Класс-ассоциация

 

Свойства класса-ассоциации характеризуют не один, а пару объектов, в данном случае — пару экземпляров, Профессор и Университет.

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

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

q       вызывают операции поставщика;

q       имеют сигнатуры, в которых возвращаемое значение или аргументы принадлежат классу поставщика.

Например, на рис. 11.12 показана зависимость класса Заказ от класса Книга, так как Книга используется в операциях проверкаДоступности, добавить и удалить класса Заказ.

 

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

 

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

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

Как показано на рис. 11.13, подкласс Летающий шкаф является наследником суперклассов Летающий предмет и Хранилище вещей. Этому подклассу достаются в наследство все свойства и операции двух классов-родителей.

Множественное наследование достаточно сложно и коварно, имеет много «подводных камней». Например, подкласс Яблочный_Пирог не следует производить от суперклассов Пирог и Яблоко. Это типичное неправильное использование множественного наследования: потомок наследует все свойства от его родителя, хотя обычно не все свойства применимы к потомку. Очевидно, что Яблочный_Пирог является Пирогом, но не является Яблоком, так как пироги не растут на деревьях.

 

Рис. 11.13. Множественное наследование

 

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

 

Рис. 11.14. Ромбовидная решетка наследования

 

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

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

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

 

Рис. 11.15. Реализация интерфейса

 

Интерфейс Обработчик каталога позволяет клиентам взаимодействовать с объектами класса Каталог без знания той дисциплины доступа, которая здесь реализована (LIFO — последний вошел, первый вышел; FIFO — первый вошел, первый вышел и т. д.).


Деревья наследования

 

При использовании отношений обобщения строится иерархия классов. Некоторые классы в этой иерархии могут быть абстрактными. Абстрактным называют класс, который не может иметь экземпляров. Имена абстрактных классов записываются курсивом. Например, на рис. 11.16 показаны абстрактные классы Млекопитающие, Собаки, Кошки.

 

Рис. 11.16. Абстрактность и полиморфизм

 

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

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

Иногда полезно отметить корневой класс, который не может иметь родителей. Такой класс помечается теговой величиной (характеристикой) root, записываемой за именем класса. Например, на рисунке показан корневой класс Млекопитающие.

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

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

 

В качестве первого примера на рис. 11.17 показана диаграмма классов системы управления полетом летательного аппарата.

 

Рис. 11.17. Диаграмма классов системы управления полетом

 

Здесь представлен класс ПрограммаПолета, который имеет свойство ТраекторияПолета, операцию-модификатор ВыполнятьПрограмму () и операцию-селектор ПрогнозОкончУправления (). Имеется ассоциация между этим классом и классом Контроллер СУ — экземпляры программы задают параметры движения, которые должны обеспечивать экземпляры контроллера.

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

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

Для класса Контроллер СУ задано ограничение на множественность — допускается не более трех экземпляров этого класса. Класс Регулятор скорости имеет ограничение другого типа — повторное включение его экземпляра разрешается не раньше, чем через 64 мс.

В качестве второго примера на рис. 11.18 приведена диаграмма классов для информационной системы театра. Эту систему образует 6 классов.

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

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

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

Между классами Спектакль и Зритель тоже определена ассоциация. Она поясняет, что зритель может смотреть любое число спектаклей, а на каждом спектакле может быть любое число зрителей.

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

 

Рис. 11.18. Диаграмма классов информационной системы театра

 

Контрольные вопросы

 

1.      Поясните назначение статических моделей объектно-ориентированных программных систем.

2.      Что является основным средством для представления статических моделей?

3.      Как используются статические модели?

4.      Какие секции входят в графическое обозначение класса?

5.      Какие секции класса можно не показывать?

6.      Какие имеются разновидности области действия свойства (операции)?

7.      Поясните общий синтаксис представления свойства.

8.      Какие уровни видимости вы знаете? Их смысл?

9.      Какие характеристики свойств вам известны?

10.  Поясните общий синтаксис представления операции.

11.  Какой вид имеет форма представления параметра операции?

12.  Какие характеристики операций вам известны?

13.  Что означают три точки в списке свойств (операций)?

14.  Как организуется группировка свойств (операций)?

15.  Как ограничить количество экземпляров класса?

16.  Перечислите известные вам «украшения» отношения ассоциации.

17.  Может ли статическая модель программной системы не иметь отношений ассоциации?

18.  Какой смысл имеет квалификатор? К чему он относится?

19.  Какие отношения могут иметь пометки видимости и что эти пометки обозначают?

20.  Какой смысл имеет класс-ассоциация?

21.  Чем отличается агрегация от композиции? Разновидностями какого отношения (в UML) они являются?

22.  Что обозначает в UML простая зависимость?

23.  Какой смысл имеет отношение обобщения?

24.  Какие недостатки у множественного наследования?

25.  Перечислите недостатки ромбовидной решетки наследования.

26.  В чем смысл отношения реализации?

27.  Что обозначает мощность «многие-ко-многим» и в каких отношениях она применяется?

28.  Что такое абстрактный класс (операция) и как он (она) отображается?

29.  Как запретить полиморфизм операции?

30.  Как обозначить корневой класс?

ГЛАВА 12. Динамические модели объектно-ориентированных программных систем

 

Динамические модели обеспечивают представление поведения систем. «Динамизм» этих моделей состоит в том, что в них отражается изменение состояний в процессе работы системы (в зависимости от времени). Средства языка UML для создания динамических моделей многочисленны и разнообразны [8], [23], [41], [53], [67]. Эти средства ориентированы не только на собственно программные системы, но и на отображение требований заказчика к поведению таких систем.

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

 

Для моделирования поведения системы используют:

q       автоматы;

q       взаимодействия.

Автомат (State machine) описывает поведение в терминах последовательности состояний, через которые проходит объект в течение своей жизни. Взаимодействие (Interaction) описывает поведение в терминах обмена сообщениями между объектами.

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

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

Автоматы отображают с помощью:

q       диаграмм схем состояний;

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

Взаимодействия отображают с помощью:

q       диаграмм сотрудничества (кооперации);

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

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

 

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

Диаграмма схем состояний показывает:

1) набор состояний системы;

2) события, которые вызывают переход из одного состояния в другое;

3) действия, которые происходят в результате изменения состояния.

В языке UML состоянием называют период в жизни объекта, на протяжении которого он удовлетворяет какому-то условию, выполняет определенную деятельность или ожидает некоторого события. Как показано на рис. 12.1, состояние изображается как закругленный прямоугольник, обычно включающий его имя и подсостоя-ния (если они есть).

 

Рис. 12.1. Обозначение состояния

 

Переходы между состояниями отображаются помеченными стрелками (рис. 12.2).

 

Рис. 12.2. Переходы между состояниями

 

На рис. 12.2 обозначено: Событие — происшествие, вызывающее изменение состояния, Действие — набор операций, запускаемых событием.

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

Примеры событий:

баланс < 0

помехи

уменьшить(Давление)

after (5 seconds)

when (time = 16:30)

Изменение в состоянии

Сигнал (объект с именем)

Вызов действия

Истечение периода времени

Наступление абсолютного момента времени

 

Примеры действий:

 

Кассир. прекратитьВыплаты( )

flt:= new(Фильтp); Ш.убратьПомехи( )

send Ник. привет

Вызов одной операции

Вызов двух операций

Посылка сигнала в объект Ник

ПРИМЕЧАНИЕ

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

 

Для отображения перехода в начальное состояние принято обозначение, показанное на рис. 12.3.

 

Рис. 12.3. Переход в начальное состояние

 

Соответственно, обозначение перехода в конечное состояние имеет вид, представленный на рис. 12.4.

 

Рис. 12.4. Переход в конечное состояние

 

В качестве примера на рис. 12.5 показана диаграмма схем состояний для системы охранной сигнализации.

 

Рис. 12.5. Диаграмма схем состояний системы охранной сигнализации

 

Из рисунка видно, что система начинает свою жизнь в состоянии Инициализация, затем переходит в состояние Ожидание. В этом состоянии через каждые 10 секунд (по событию after (10 sec.)) выполняется самопроверка системы (операция Самопроверка ()). При наступлении события Тревога (Датчик) реализуются действия, связанные с блокировкой периметра охраняемого объекта, — исполняется операция БлокироватьПериметр() и осуществляется переход в состояние Активна. В активном состоянии через каждые 5 секунд по событию after (5 sec.) запускается операция ПриемКоманды(). Если команда получена (наступило событие Сброс), система возвращается в состояние Ожидание. В процессе возврата разблокируется периметр охраняемого объекта (операция РазблокироватьПериметр()).


Действия в состояниях

 

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

Например, как показано на рис. 12.6, при входе в состояние Активна выполняется операция УстановитьТревогу() из класса Контроллер, а при выходе из состояния — операция СбросТревоги().

 

Рис. 12.6. Входные и выходные действия и деятельность в состоянии Активна

 

Действие, которое должно выполняться, когда система находится в данном состоянии, указывается после метки do. Считается, что такое действие начинается при входе в состояние и заканчивается при выходе из него. Например, в состоянии Активна это действие ПодтверждатьТревогу().

Условные переходы

 

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

Правила пометки стрелок условных переходов иллюстрирует рис. 12.7.

 

Рис. 12.7. Обозначение условного перехода

 

Порядок выполнения условного перехода:

1)      происходит событие;

2)      вычисляется условие УсловиеПерехода;

3)      при УсловиеПерехода=true запускается переход и активизируется действие, в противном случае переход не выполняется.

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

 

Рис. 12.8. Условный переход между состояниями


Вложенные состояния

 

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

 

Рис. 12.9. Обозначение подсостояний

 

На рис. 12.10 приведена внутренняя структура составного состояния Активна.

 

Рис. 12.10. Переходы в состоянии Активна

 

Семантика вложенности такова: если система находится в состоянии Активна, то она должна быть точно в одном из подсостояний: Проверка, Звонок, Ждать. В свою очередь, в подсостояние могут вкладываться другие подсостояния. Степень вложенности подсостояний не ограничивается. Данная семантика соответствует случаю последовательных подсостояний.

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

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

 

Рис. 12.11. Историческое состояние

 

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

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

 

Рис. 12.12. Символ состояния со скрытыми подсостояниями

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

 

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

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

 

Рис. 12.13. Состояние действия

 

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

 

Рис. 12.14. Состояние под-деятельности

 

Фактически в данную вершину вписывается имя другой диаграммы, имеющей внутреннюю структуру.

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

Кроме того, в диаграммах деятельности используются вспомогательные вершины:

q       решение (ромбик с одной входящей и несколькими исходящими стрелками);

q       объединение (ромбик с несколькими входящими и одной исходящей стрелкой);

q       линейка синхронизации — разделение (жирная горизонтальная линия с одной входящей и несколькими исходящими стрелками);

q       линейка синхронизации — слияние (жирная горизонтальная линия с несколькими входящими и одной исходящей стрелкой);

q       начальное состояние (черный кружок);

q       конечное состояние (незакрашенный кружок, в котором размещен черный кружок меньшего размера).

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

Вершина «объединение» отмечает точку слияния альтернативных потоков действий.

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

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

 

Рис. 12.15. Диаграмма деятельности покупателя в Интернет-магазине

 

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


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

 

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

Диаграммы сотрудничества

 

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

Обозначение объекта показано на рис. 12.16.

 

Рис. 12.16. Обозначение объекта

 

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

ИмяОбъекта : ИмяКласса

Примеры записи имени:

Адам : Человек

: Пользователь

мойКомпьютер

агент :

Имя объекта и класса

Только имя класса (анонимный объект)

Только имя объекта (подразумевается, что имя класса известно)

Объект — сирота (подразумевается, что имя класса неизвестно)

 

Синтаксис представления свойства имеет вид

Имя : Тип = Значение

Примеры записи свойства:

 

номер:Телефон = "7350-420"

активен = True

Имя, тип, значение

Имя и значение

 

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

Итак, связь — это путь для пересылки сообщения. Путь может быть снабжен характеристикой видимости. Характеристика видимости проставляется как стандартный стереотип над дальним концом связи. В языке предусмотрены следующие стандартные стереотипы видимости:

 

«global»

«local»

«parameter»

«self»

Объект-поставщик находится в глобальной области определения

Объект-поставщик находится в локальной области определения объекта-клиента

Объект-поставщик является параметром операции объекта-клиента

Один и тот же объект является и клиентом, и поставщиком

 

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

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

 

Вызов

Возврат

Посылка(Send)

Создание

Уничтожение

В объекте запускается операция

Возврат значения в вызывающий объект

В объект посылается сигнал

Создание объекта, выполняется по стандартному сообщению «create»

Уничтожение объекта, выполняется по стандартному сообщению «destroy»

 

Для записи сообщений в языке UML принят следующий синтаксис:

ВозврВеличина := ИмяСообщения (Аргументы),

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

Примеры записи сообщений:

 

Коорд := ТекущПоложение(самолетТ1)

оповещение( )

УстановитьМаршрут(х)

«create»

Вызов операции, возврат значения

Посылка сигнала

Вызов операции с действительным параметром

Стандартное сообщение для создания объекта

 

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

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

 

Рис. 12.17. Поток синхронных сообщений

 

Здесь сообщение 2.1 : Напиток : = Изготовить(Смесь№3) определено как первое сообщение, вложенное во второе сообщение 2 : Заказать(Смесь№3) последовательности, а сообщение 2.2 : Принести(Напиток) — как второе вложенное сообщение. Все сообщения процедурной последовательности считаются синхронными. Работа с синхронным сообщением подчиняется следующему правилу: передатчик ждет до тех пор, пока получатель не примет и не обработает сообщение. В нашем примере это означает, что третье сообщение будет послано только после обработки сообщений 2.1 и 2.2. Отметим, что степень вложенности сообщений может быть любой. Главное, чтобы соблюдалось правило: последовательность сообщений внешнего уровня возобновляется только после завершения вложенной последовательности.

Менее общую форму управления задает асинхронный поток управления. Как показано на рис. 12.18, асинхронный поток рисуется обычными стрелками. Здесь все сообщения считаются асинхронными, при которых передатчик не ждет реакции от получателя сообщения. Такой вид коммуникации имеет семантику почтового ящика — получатель принимает сообщение по мере готовности. Иными словами, передатчик и получатель не синхронизируют свою работу, скорее, один объект «избавляется» от сообщения для другого объекта. В нашем примере сообщение ПодтверждениеВызова определено как второе сообщение в последовательности.

 

Рис. 12.18. Поток асинхронных сообщений

 

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

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

*[i := 1 .. n].

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

1*[1 := 1 .. 4] : РисоватьСторонуПрямоугольника(i)

Для моделирования ветвления после номера сообщения добавляется выражение условия, например: [х>0]. Сообщение альтернативной ветви помечается таким же номером, но с другим условием: [х<=0]. Пример итерационного и разветвляющегося потока сообщений приведен на рис. 12.19.

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

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

1) отображаются объекты, которые участвуют во взаимодействии;

2) рисуются связи, соединяющие эти объекты;

3) связи помечаются сообщениями, которые посылают и получают выделенные объекты.

 

Рис. 12.19. Итерация и ветвление

 

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

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

 

Рис. 12.20. Диаграмма сотрудничества системы управления полетом

 

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

q       ВклРегСкор( );

q       ВрРегУгл();

q       ТекущСкор();

q       ТекущУгл( ).

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

q       включение Регулятора Скорости происходит, если относительное время полета Т больше заданного периода Тзад;

q       включение Регулятора Углов обеспечивается, если относительное время поле-; та меньше или равно заданному периоду.


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

 

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

Графически диаграмма последовательности — разновидность таблицы, которая показывает объекты, размещенные вдоль оси X, и сообщения, упорядоченные по времени вдоль оси Y.

 

Рис. 12.21. Диаграмма последовательности системы управления полетом

 

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

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

Первая характеристика — линия жизни объекта.

Линия жизни объекта — это вертикальная пунктирная линия, которая обозначает период существования объекта. Большинство объектов существуют на протяжении всего взаимодействия, их линии жизни тянутся от вершины до основания диаграммы. Впрочем, объекты могут создаваться в ходе взаимодействия. Их линии жизни начинаются с момента приема сообщения «create». Кроме того, объекты могут уничтожаться в ходе взаимодействия. Их линии жизни заканчиваются с момента приема сообщения «destroy». Как представлено на рис. 12.22, уничтожение линии жизни отмечается пометкой X в конце линии:

 

Рис. 12.22. Создание и уничтожение объекта

 

Вторая характеристика — фокус управления.

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

 

Рис. 12.23. Вложение фокусов управления

Замечания.

1.      Для отображения «условности» линия жизни может быть разделена на несколько параллельных линий жизни. Каждая отдельная линия соответствует условному ветвлению во взаимодействии. Далее в некоторой точке линии жизни могут быть снова слиты (рис. 12.24).

 

Рис. 12.24. Параллельные линии жизни

 

2.      Ветвление показывается множеством стрелок, идущих из одной точки. Каждая стрелка отмечается сторожевым условием (рис. 12.25).

 

Рис. 12.25. Ветвление

 

Диаграммы Use Case

 

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

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


Актеры и элементы Use Case

 

Вершинами в диаграмме Use Case являются актеры и элементы Use Case. Их обозначения показаны на рис. 12.26.

Актеры представляют внешний мир, нуждающийся в работе системы. Элементы Use Case представляют действия, выполняемые системой в интересах актеров.

 

Рис. 12.26. Обозначения актера и элемента Use Case

Актер — это роль объекта вне системы, который прямо взаимодействует с ее частью — конкретным элементом (элементом Use Case). Различают актеров и пользователей. Пользователь — это физический объект, который использует систему. Он может играть несколько ролей и поэтому может моделироваться несколькими актерами. Справедливо и обратное — актером могут быть разные пользователи.

Например, для коммерческого летательного аппарата можно выделить двух актеров: пилота и кассира. Сидоров — пользователь, который иногда действует как пилот, а иногда — как кассир. Как изображено на рис. 12.27, в зависимости от роли Сидоров взаимодействует с разными элементами Use Case.

 

Рис. 12.27. Модель Use Case

Элемент Use Case — это описание последовательности действий (или нескольких последовательностей), которые выполняются системой и производят для отдельного актера видимый результат.

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

Отношения в диаграммах Use Case

 

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

 

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

 

Между актерами допустимо отношение обобщения (рис. 12.29), означающее, что экземпляр потомка может взаимодействовать с такими же разновидностями экземпляров элементов Use Case, что и экземпляр родителя.

Рис. 12.29. Отношение обобщения между актерами

 

Между элементами Use Case определены отношение обобщения и две разновидности отношения зависимости — включения и расширения.

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

 

Рис. 12.30. Отношение обобщения между элементами Use Case

 

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

 

Рис. 12.31. Отношение включения между элементами Use Case

 

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

 

Рис. 12.32. Отношение расширения между элементами Use Case

 

 

Рис. 12.33. Простейшая диаграмма Use Case для банка

 

Рис. 12.34. Диаграмма Use Case для обслуживания заказчика

 

Пример простейшей диаграммы Use Case, в которой использованы отношения включения и расширения, приведен на рис. 12.33.

Как показано на рис. 12.34, внутри элемента Use Case может быть дополнительная секция с заголовком Extention points. В этой области перечисляются точки расширения. В указанную здесь точку дополнительные запросы вставляется последовательность действий от расширяющего элемента Use Case Запрос каталога. Для справки отмечено, что точка расширения размещена после действий, обеспечивающих создание заказа. На этом же рисунке отображены отношения наследования между элементами Use Case. Видно, что элементы Use Case Оплата наличными и Оплата в кредит наследуют поведение элемента Use Case Произвести оплату и являются его специализациями.


Работа с элементами Use Case

 

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

Поведение элемента Use Case описывается потоком событий. Начальное описание выполняется в текстовой форме, прозрачной для пользователя системы. В потоке событий выделяют:

q       основной поток и альтернативные потоки поведения;

q       как и когда стартует и заканчивается элемент Use Case;

q       когда элемент Use Case взаимодействует с актерами;

q       какими данными обмениваются актер и система.

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

В общем случае один элемент Use Case описывает набор последовательностей, в котором каждая последовательность представляет возможный поток событий. Каждая последовательность называется сценарием. Сценарий — конкретная последовательность действий, которая иллюстрирует поведение. Сценарии являются для элемента Use Case почти тем же, чем являются экземпляры для класса. Говорят, что сценарий — это экземпляр элемента Use Case.

Спецификация элементов Use Case

 

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

Предусловие: перед началом этого элемента Use Case должен быть выполнен элемент Use Case «Заполнить базу данных авиарейсов».

Главный поток

 

Этот элемент Use Case начинается, когда покупатель регистрируется в системе и вводит свой пароль. Система проверяет, правилен ли пароль (Е-1), и предлагает покупателю выбрать одно из действий: СОЗДАТЬ, УДАЛИТЬ, ПРОВЕРИТЬ, ВЫПОЛНИТЬ, ВЫХОД.

1. Если выбрано действие СОЗДАТЬ, выполняется подпоток S-1: создать заказ авиабилета.

2. Если выбрано действие УДАЛИТЬ, выполняется подпоток S-2: удалить заказ авиабилета.

3. Если выбрано действие ПРОВЕРИТЬ, выполняется подпоток S-3: проверить заказ авиабилета.

4. Если выбрано действие ВЫПОЛНИТЬ, выполняется подпоток S-4: реализовать заказ авиабилета.

5. Если выбрано действие ВЫХОД, элемент Use Case заканчивается.


Подпотоки

S-1: создать заказ авиабилета. Система отображает диалоговое окно, содержащее поля для пункта назначения и даты полета. Покупатель вводит пункт назначения и дату полета (Е-2). Система отображает параметры авиарейсов (Е-3). Покупатель выбирает авиарейс. Система связывает покупателя с выбранным авиарейсом (Е-4). Возврат к началу элемента Use Case.

S-2: удалить заказ авиабилета. Система отображает параметры заказа. Покупатель подтверждает решение о ликвидации заказа (Е-5). Система удаляет связь с покупателем (Е-6). Возврат к началу элемента Use Case.

S-3: проверить заказ авиабилета. Система выводит (Е-7) и отображает параметры заказа авиабилета: номер рейса, пункт назначения, дата, время, место, цену. Когда покупатель указывает, что он закончил проверку, выполняется возврат к началу элемента Use Case.

S-4: реализовать заказ авиабилета. Система запрашивает параметры кредитной карты покупателя. Покупатель вводит параметры своей кредитной карты (Е-8). Возврат к началу элемента Use Case.

Альтернативные потоки

 

Е-1: введен неправильный ID-номер покупателя. Покупатель может повторить ввод ID-номера или прекратить элемент Use Case.

Е-2: введены неправильные пункт назначения/дата полета. Покупатель может повторить ввод пункта назначения/даты полета или прекратить элемент Use Case.

Е-3: нет подходящих авиарейсов. Покупатель информируется, что в данное время такой полет невозможен. Возврат к началу элемента Use Case.

Е-4: не может быть создана связь между покупателем и авиарейсом. Информация сохраняется, система создаст эту связь позже. Элемент Use Case продолжается.

Е-5: введен неправильный номер заказа. Покупатель может повторить ввод правильного номера заказа или прекратить элемент Use Case.

Е-6: не может быть удалена связь между покупателем и авиарейсом. Информация сохраняется, система будет удалять эту связь позже. Элемент Use Case продолжается.

Е-7: система не может вывести информацию заказа. Возврат к началу элемента Use Case.

Е-8: некорректные параметры кредитной карты. Покупатель может повторить ввод параметров карты или прекратить элемент Use Case.

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


Пример диаграммы Use Case

 

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

Пример диаграммы Use Case, в которой использованы отношения включения и расширения, приведен на рис. 12.35.

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

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

q       для расширяющего элемента Use Case Состояние — это условие запрос состояния;

q       для расширяющего элемента Use Case Снять — это условие запрос снятия;

q       для расширяющего элемента Use Case Захват карты — это условие список подозрений.

 

Рис. 12.35. Использование включения и расширения

 

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

Стрелки расширения в диаграмме подписаны. Помимо стереотипа, здесь указаны:

q       в круглых скобках — имена точек расширения;

q       в квадратных скобках — условие расширения.

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

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

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

Спецификации элементов Use Case рассматриваемой диаграммы имеют следующий вид:

Элемент Use Case Сеанс банкомата

 

include (Идентификация клиента)

include (Проверка счета)

(диалог возможен)

напечатать заголовок квитанции

(выдача квитанции)

конец сеанса

//включение

//включение

//первая точка расширения

 

//вторая точка расширения

 

Расширяющий элемент Use Case Состояние

сегмент

//начало первого сегмента

принять запрос состояния

//условие расширения

отобразить информацию о состоянии счета

 

сегмент

//вторая точка расширения

конец сеанса

 

 

Расширяющий элемент Use Case Снять

 

сегмент

//начало первого сегмента

принять запрос снятия

//условие расширения

определить сумму

 

(проверка снятия)

//точка расширения

сегмент

//начало второго сегмента

напечатать снимаемую сумму

 

выдать наличные деньги

 

Расширяющий элемент Use Case Захват карты

 

сегмент

принять список подозрений

проглотить карту

конец сеанса

//начало единственного сегмента

//условие расширения

Включаемый элемент Use Case Идентификация клиента

 

получить имя клиента

include (Проверить достоверность)

получить номер счета клиента

//включение

Включаемый элемент Use Case Проверка счета

 

установить соединение с базой данных счетов

получить состояние и ограничения счета

Включаемый элемент Use Case Проверить достоверность

 

установить соединение с базой данных клиентов

получить параметры клиента

(проверка) //точка расширения

 

Опишем возможное поведение модели, задаваемое этой диаграммой.

Актер Клиент инициирует действия базового элемента Use Case Сеанс банкомата. На первом шаге запускается включаемый элемент Use Case Идентификация клиента. Этот элемент Use Case получает имя клиента и запускает элемент Use Case Проверить достоверность, в результате чего устанавливается соединение с базой данных клиентов и получаются параметры клиента.

Если к этому моменту исполняется условие расширения список подозрений, то «срабатывает» расширяющий элемент Use Case Захват карты, карта арестовывается и работа системы прекращается.

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

Базовый элемент Use Case переходит ко второму шагу работы — вызывает включаемый элемент Use Case Проверка счета, который устанавливает соединение с базой данных счетов и получает состояние и ограничения счета.

Управление опять возвращается к базовому элементу Use Case. Базовый элемент Use Case переходит к первой точке расширения диалог возможен. В этой точке возможно подключение одного из двух расширяющих элементов Use Case.

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

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

В последний раз управление возвращается к базовому элементу Use Case — завершается сеанс работы банкомата.


Построение модели требований

 

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

Выбор актеров

 

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

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

Таким образом, внешняя среда машины утилизации имеет вид, представленный на рис. 12.36.

 

Рис. 12.36. Внешняя среда машины утилизации

 

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


Определение элементов
Use Case

 

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

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

Элемент Use Case — это последовательность взаимодействий в диалоге, выполняемом актером и системой. Запускается элемент Use Case актером, поэтому удобно выявлять элементы Use Case с помощью актеров.

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

Перейдем к примеру. Потребитель — первичный актер, поэтому начнем с этой роли. Этот актер должен выполнять возврат утилизируемых элементов. Так формируется элемент Use Case Возврат элемента. Приведем его текстовое описание:

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

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

Следующий актер — Оператор. Он получает дневной отчет об элементах, сданных за день. Это образует элемент Use Case Создание дневного отчета. Его описание:

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

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

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

Кроме того, актер Оператор может изменять параметры сдаваемых элементов. Назовем соответствующий элемент Use Case Изменение элемента. Его описание:

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

После выявления всех элементов диаграмма Use Case для системы принимает вид, показанный на рис. 12.37.

 

Рис. 12.37. Диаграмма Use Case для машины утилизации

 

Чаще всего полные описания элементов Use Case формируются за несколько итераций. На каждом шаге в описание вводятся дополнительные детали. Например, окончательное описание Возврата элемента может иметь следующий вид:

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

Если тип недопустим, то на панели машины высвечивается «недействительно».

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

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

Обычно элемент Use Case задает одну основную и несколько альтернативных последовательностей событий.

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

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

Расширение функциональных возможностей

 

Для добавления в элемент Use Case новых действий удобно применять отношение расширения. С его помощью базовый элемент Use Case может быть расширен новым элементом Use Case.

В нашем примере поведение системы не определено для случая, когда сдаваемый элемент застрял. Введем элемент Use Case Элемент Застрял, который будет расширять базовый элемент Use Case Возврат Элемента (рис. 12.38).

 

Рис 12.38. Расширение элемента Use Case возврат элемента

 

Описание элемента Use Case Элемент застрял может иметь следующий вид:

Если элемент застрял, для вызова Оператора вырабатывается сигнал тревоги. После удаления застрявшего элемента Оператор сбрасывает сигнал тревоги. В результате Потребитель может продолжить сдачу элементов. Величина ИТОГО сохраняет правильное значение. Цена застрявшего элемента не засчитывается.

Таким образом, описание базового элемента остается прежним, простым. Еще один пример приведен на рис. 12.39.

Здесь мы видим только один базовый элемент Use Case Сеанс работы. Все остальные элементы Use Case могут добавляться как расширения. Базовый элемент Use Case при этом остается почти без изменений.

 

Рис. 12.39. Применение отношения расширения

 

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

В расширяющем элементе Use Case указывается ссылка на то место базового элемента Use Case, куда он будет вставляться (при прерывании). После выполнения расширяющего элемента Use Case продолжается выполнение базового элемента Use Case.

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

q       для моделирования вариантных частей элементов Use Case;

q       для моделирования сложных и редко выполняемых альтернативных последовательностей;

q       для моделирования подчиненных последовательностей, которые выполняются только в определенных случаях;

q       для моделирования систем с выбором на основе меню.

Главное, что следует помнить: решение о выборе, подключении варианта на основе расширения принимается вне базового элемента Use Case. Если же вы вводите в базовый элемент Use Case условную конструкцию, конструкцию выбора, то придется применять отношение включения. Это случай, когда «штурвал управления» находится в руках базового элемента Use Case.


Уточнение модели требований

 

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

Извлеченные элементы Use Case называют абстрактными. Они не могут быть конкретизированы сами по себе, применяются для описания одинаковых частей в других, конкретных элементах Use Case. Таким образом, описания абстрактных элементов Use Case используются в описаниях конкретных элементов Use Case. Говорят, что конкретный элемент Use Case находится в отношении «включает» с абстрактным элементом Use Case.

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

 

Рис. 12.40. Применение отношения включения

 

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

Выделение абстрактных элементов Use Case можно упростить с помощью абстрактных актеров.

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

 

Рис. 12.41. Выделение абстрактного актера

Выводы:

 

1.      Абстрактные элементы Use Case находят извлечением общих последовательностей из различных элементов Use Case.

2.      Отношение «включает» применяется, если несколько элементов Use Case имеют общее поведение. Цель: устранить повторения, ликвидировать избыточность.

3.      Кроме того, это отношение часто используют для ограничения сложности большого элемента Use Case.

4.      Отношение «расширяет» применяется, когда описывается вариация, дополняющая нормальное поведение.


Кооперации и паттерны

 

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

В терминологии фирмы Rational (вдохновителя и организатора побед языка UML) кооперации называют реализациями элементов Use Case, да и обозначения их весьма схожи (рис. 12.42).

 

Рис. 12.42. Элемент Use Case и его реализация

 

Обратите внимание на то, что и связаны эти элементы отношением реализации: кооперация реализует конкретный элемент Use Case.

Кооперации содержат две составляющие — статическую (структурную) и динамическую (поведенческую).

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

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

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

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

 

Рис. 12.43. Динамическая составляющая кооперации Заказ авиабилета

 

 

Рис. 12.44. Статическая составляющая кооперации Заказ авиабилета

 

 

Рис. 12.45. Обозначение паттерна

 

Параметризованные, то есть настраиваемые кооперации называют паттернами (образцами). Паттерн является решением типичной проблемы в определенном контексте. Обозначение паттерна имеет вид, представленный на рис. 12.45.

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

Паттерны рассматриваются как крупные строительные блоки. Их использование приводит к существенному сокращению затрат на анализ и проектирование ПО. повышению качества и правильности разработки на логическом уровне, ведь паттерны создаются опытными профессионалами и отражают проверенные и оптимизированные решения [26], [31], [68].

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

Наиболее распространенные паттерны формализуют и сводят в единые каталоги. Самым известным каталогом проектных паттернов, обеспечивающих этап проектирования ПО, считают каталог «Команды четырех» (Э. Гамма и др.). Он включает в себя 23 паттерна, разделенные на три категории [31]. Как показано в табл. 12.1, по мнению «Команды четырех», описание паттерна должно состоять из четырех основных частей.

Таблица 12.1. Описание паттерна

Раздел

Описание

Имя

 

 

Проблема

 

Решение

 

 

 

Результаты

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

Формулируется проблема проектирования (и ее контекст), на которую ориентировано применение паттерна. Задаются условия применения

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

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

 

Обсудим применение нескольких паттернов из каталога «Команды четырех».


Паттерн Наблюдатель

Паттерн Наблюдатель (Observer) задает между объектами такую зависимость «один-ко-многим», при которой изменение состояния одного объекта приводит к оповещению и автоматическому обновлению всех зависящих от него объектов.

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

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

q       когда при изменении состояния одного объекта должны изменить свое состояние все зависимые объекты, причем количество зависимых объектов заранее неизвестно;

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

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

 

Рис. 12.46. Различные графические отображения состояния субъекта

 

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

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

ПРИМЕЧАНИЕ

Курсивом в данном абзаце отображены имена абстрактных классов и операций (это требование языка UML).

 

Динамическая составляющая паттерна Наблюдатель показана на рис. 12.48. На рисунке представлено поведение паттерна при взаимодействии субъекта с двумя наблюдателями.

 

Рис. 12.47. Структурная составляющая паттерна Наблюдатель

 

 

Рис. 12.48. Динамическая составляющая паттерна Наблюдатель

 

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

Обозначение паттерна Наблюдатель приведено на рис. 12.49, где показано, что у него два параметра настройки — субъект и наблюдатель.

 

Рис. 12.49. Обозначение паттерна Наблюдатель

 

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

 

Рис. 12.50. Настройка паттерна Наблюдатель

 

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


Паттерн Компоновщик

 

Паттерн Компоновщик (Composite) обеспечивает представление иерархий часть-целое, объединяя объекты в древовидные структуры.

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

q       необходимо построить иерархию объектов вида часть-целое;

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

Решение. Ключевым элементом решения является абстрактный класс Компонент, который является одновременно и примитивом, и контейнером. В нем объявлены:

q       абстрактная операция примитива Работать( );

q       абстрактные операции контейнера — управления примитивами-потомками Добавить(Компонент) и Удалить(Компонент), а также доступа к потомку Получить-Потомка().

Структурная составляющая паттерна Компоновщик представлена на рис. 12.51.

 

Рис. 12.51. Структурная составляющая паттерна Компоновщик

 

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

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

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

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

Обозначение паттерна Компоновщик приведено на рис. 12.52, где показано, что у него три параметра настройки — компонент, компоновщик и лист.

Настройку паттерна на графическое приложение иллюстрирует рис. 12.53.

 

Рис. 12.52. Обозначение паттерна Компоновщик

 

 

Рис. 12.53. Настройка паттерна Компоновщик

 

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


Паттерн Команда

 

Паттерн Команда (Command) выполняет преобразование запроса в объект, обеспечивая:

q       параметризацию клиентов с различными запросами;

q       постановку запросов в очередь и их регистрацию;

q       поддержку отмены операций.

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

q       объекты параметризируются действием. В процедурных языках параметризация осуществляется при помощи функции обратного вызова, которая регистрируется для последующего вызова. Паттерн Команда предлагает объектно-ориентированную замену функций обратного вызова;

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

q       необходимо регистрировать изменения состояния для восстановления системы в случае аварийного отказа;

q       необходимо создание сложных операций, которые строятся на основе примитивных операций.

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

Структурная составляющая паттерна Команда показана на рис. 12.54. Классы этой структуры имеют следующие обязанности:

q       Команда объявляет интерфейс для выполнения операции;

q       КонкрКоманда определяет связь между экземпляром класса Получатель и действием, реализует Выполнять(), вызывая нужную операцию получателя;

q       Клиент создает объект класса КонкрКоманда и устанавливает его получателя;

q       Инициатор просит команду выполнить запрос;

q       Получатель умеет выполнять запрашиваемые операции.

 

Рис. 12.54. Структурная составляющая паттерна Команда

 

В качестве конкретной команды могут выступать команда Открыть, команда Вставить. Инициатором может быть Пункт Меню, а получателем — Документ.

Объекты этого паттерна осуществляют следующие взаимодействия:

q       клиент создает объект класса КонкрКоманда и задает его получателя;

q       объект класса Инициатор сохраняет объект класса КонкрКоманда;

q       инициатор вызывает операцию Выполнять() объекта класса КонкрКоманда;

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

Результаты. Применение паттерна Команда приводит к следующему:

q       объект, запрашивающий операцию, отделяется от объекта, умеющего выполнять запрос;

q       объекты-команды являются полноценными объектами. Их можно использовать и расширять обычным способом;

q       из простых команд легко компонуются составные команды;

q       легко добавляются новые команды (изменять существующие классы при этом не требуется).

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

 

Рис. 12.55. Обозначение паттерна Команда

 

Настройку паттерна на приложение с графическим меню иллюстрирует рис. 12.56.

 

Рис. 12.56. Настройка паттерна Команда

 

Очевидно, что в получаемой кооперации конкретный класс Редактор играет роль клиента, классы КомандаОткрыть и КомандаВставить становятся классами Конкретных Команд (и подклассами абстрактного класса Команда), класс ПунктМеню замещает класс Инициатор паттерна, а конкретный класс Документ замещает класс Получатель паттерна.


Бизнес-модели

 

Достаточно часто перед тем, как решиться на заказ ПО, организация проводит бизнес-моделирование. Цели бизнес-моделирования:

q       отобразить структуру и процессы деятельности организации;

q       обеспечить ясное, комплексное и, главное, одинаковое понимание нужд организации как сотрудниками, так и будущими разработчиками ПО;

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

Для достижения этих целей разрабатываются две модели: Q бизнес-модель Use Case; а бизнес-объектная модель.

Бизнес-модель Use Case задает внешнее представление бизнес-процессов организации (с точки зрения внешней среды — клиентов и партнеров).

Как показано на рис. 12.57, бизнес-модель Use Case строится с помощью бизнес-актеров и бизнес-элементов Use Case — простого расширения средств, используемых в обычных диаграммах Use Case.

 

Рис. 12.57. Фрагмент бизнес-модели Use Case для аэропорта

 

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

Бизнес-элементы Use Case изображают различные рабочие потоки бизнеса. Последовательности действий в бизнес-элементах Use Case обычно описываются диаграммами деятельности.

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

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

 

Рис. 12.58. Фрагмент бизнес-объектной модели аэропорта

 

Бизнес-работник — абстракция человека, действующего в бизнесе. Бизнес-сущности являются «предметами», обрабатываемыми или используемыми бизнес-работниками по мере выполнения бизнес-элемента Use Case. Например, бизнес-сущность представляет собой документ или существенную часть продукта. Фактически бизнес-объектная модель отображается с помощью диаграмм классов.

Контрольные вопросы

 

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

2.               Охарактеризуйте вершины и дуги диаграммы схем состояний. В чем состоит назначение этой диаграммы?

3.               Как отображаются действия в состояниях диаграммы схем состояний?

4.               Как показываются условные переходы между состояниями?

5.               Как задаются вложенные состояния в диаграммах схем состояний?

6.               Поясните понятие исторического подсостояния.

7.               Охарактеризуйте средства и возможности диаграммы деятельности.

8.               Когда не следует применять диаграмму деятельности?

9.               Какие средства диаграммы деятельности позволяют отобразить параллельные действия?

10.           Зачем в диаграмму деятельности введены плавательные дорожки?

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

12.           Поясните синтаксис представления свойства в диаграмме сотрудничества.

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

14.           В какой форме записываются сообщения в языке UML? Поясните смысл сообщения.

15.           В каком отношении находятся сообщения и действия? Перечислите разновидности действий.

16.           Чем отличается процедурный поток от асинхронного потока сообщений?

17.           Как указывается повторение сообщений?

18.           Как показать ветвление сообщений?

19.           Что общего в диаграмме последовательности и диаграмме сотрудничества? Чем они отличаются друг от друга?

20.           Как отображается порядок передачи сообщений в диаграмме последовательности?

21.           Когда удобнее применять диаграммы последовательности?

22.           Из каких элементов состоит диаграмма Use Case?

23.           Какие отношения разрешены между элементами диаграммы Use Case?

24.           Для чего применяют диаграммы Use Case?

25.           Чем отличаются друг от друга отношения включения и расширения с точки зрения управления?

26.           Каково назначение спецификации элемента Use Case и как она оформляется?

27.           Что такое сценарий элемента Use Case?

28.           Как документируется отношение включения?

29.           Как документируется отношение расширения?

30.           Каков порядок построения модели требований?

31.           Каково назначение кооперации? Какие составляющие ее образуют?

32.           Могут ли разные кооперации использовать одинаковые классы? Обоснуйте ответ.

33.           Что такое паттерн?

34.           Чем паттерн отличается от кооперации? Чем они схожи?

35.           Как описывается паттерн?

36.           Что нужно сделать для применения паттерна?

37.           Каковы цели бизнес-моделирования?

38.           Из каких частей состоит бизнес-модель? На что похожи эти части? В чем их своеобразие?