Язык UML. Руководство пользователя

         

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


Введение

Термины и понятия

Общие свойства

Содержание

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

Диаграммы кооперации

Семантическая эквивалентность

Типичные примеры применения

Типичные приемы моделирования

Потоки управления во времени

Структура потоков управления

Прямое и обратное проектирование

Советы



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


Введение

Термины и понятия

Общие свойства

Наполнение



Состояния действия и состояния деятельности

Переходы

Ветвление

Разделение и слияние

Дорожки

Траектория объекта

Типичные примеры применения

Типичные приемы моделирования

Рабочий процесс

Операция

Прямое и обратное проектирование

Советы



События и сигналы


Введение

Термины и понятия

Виды событий

Сигналы

События вызова

События времени и изменения

Посылка и получение событий

Типичные приемы моделирования

Семейства сигналов

Исключения

Советы



Автоматы


Введение

Термины и понятия

Контекст

Состояния

Переходы

Более сложные аспекты состояний и переходов

Подсостояния

Типичные приемы моделирования

Жизненный цикл объекта

Советы



Процессы и нити


Введение

Термины и понятия

Поток управления

Классы и события

Стандартные элементы

Коммуникация

Синхронизация

Представления с точки зрения процессов

Типичные приемы моделирования

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

Межпроцессная коммуникация

Советы



Время и пространство


Введение

Термины и понятия

Время

Местоположение

Типичные приемы моделирования

Временные ограничения

Распределение объектов

Мигрирующие объекты

Советы



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


Введение

Термины и понятия

Общие свойства

Содержание

Типичные примеры использования

Типичные приемы моделирования

Реактивные объекты

Прямое и обратное проектирование

Советы



Компоненты


Введение

Термины и понятия

Имена

Компоненты и классы

Компоненты и интерфейсы

Заменяемость двоичного кода

Виды компонентов

Организация компонентов

Стандартные элементы

Типичные приемы моделирования

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

Таблицы, файлы и документы

Интерфейс прикладного программирования

Исходный код

Советы



Развертывание


Введение

Термины и понятия

Имена

Узлы и компоненты

Организация узлов

Соединения

Типичные приемы моделирования

Процессоры и устройства

Распределение компонентов

Советы



Кооперации


Введение

Термины и понятия

Имена

Структуры

Поведение

Организация коопераций

Типичные приемы моделирования

Реализация прецедента

Реализация операции

Механизм

Советы



Образцы и каркасы


Введение

Термины и понятия

Образцы и архитектура

Механизмы

Каркасы

Типичные приемы моделирования

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

Архитектурные образцы

Советы



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


Введение

Термины и понятия

Общие свойства

Содержание

Типичные примеры применения

Типичные приемы моделирования

Исходный код

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

Физическая база данных

Адаптивные системы

Прямое и обратное проектирование

Советы



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


Введение

Термины и понятия

Общие свойства

Содержание

Типичное применение

Типичные приемы моделирования

Встроенная система

Клиент-серверная система

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

Прямое и обратное проектирование

Советы



Системы и модели


Введение

Термины и понятия

Системы и подсистемы

Модели и представления

Трассировка

Типичные приемы моделирования

Архитектура системы

Системы систем

Советы



Группирующие сущности


Группирующие сущности - это организационные составляющие моделей UML. К их числу относятся пакеты (Packages).




Группы элементов


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

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

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

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

Моделирование группы элементов производится так:

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

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


Рис. 12.6 Моделирование групп элементов

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

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


Характеристики процесса


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

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

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


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

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

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

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


Имена


У каждого класса должно быть имя, отличающее его от других классов. Имя класса - это текстовая строка. Взятое само по себе, оно называется простым именем; к составному имени спереди добавлено имя пакета, куда входит класс. Имя класса в объемлющем пакете должно быть уникальным (см. главу 12). При графическом изображении класса показывается только его имя, как на рис. 4.2.


Рис. 4.2 Простые и составные имена

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


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


Рис. 11.2 Простые и составные имена

Примечание: Имя интерфейса может состоять из любого числа букв, цифр и некоторых знаков препинания (за исключением таких, как двоеточия, которые применяются для отделения имени интерфейса от имени объемлющего пакета). Имя может занимать несколько строк. Обычно для именования интерфейсов используют одно или несколько коротких существительных, взятых из словаря моделируемой системы. Чтобы отличить интерфейс от класса, принято добавлять к его имени начальную букву I, например IUnknown или ISpelling. Те же правила применяются и к типам. Чтобы отличить тип от интерфейса или класса, принято начинать его имя с буквы Т, например TNatural или TCharacter.




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


Рис. 12.2 Простой и расширенный пакеты

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




Каждый экземпляр должен обладать именем, отличающим его в данном контексте от остальных экземпляров. Обычно объект существует в контексте операции (см. главы 4 и 9), компонента (см. главу 25) или узла (см. главу 26). Имя - это текстовая строка, например t или myCustomer на рис. 13.2. Взятое само по себе, оно называется простым именем. Абстракция экземпляра может иметь как простое имя (скажем, Transaction), так и составное (Multimedia: :AudioStream). Составное имя образуется, если к имени абстракции спереди добавить имя пакета, в котором она находится.

Объект можно именовать явно, давая ему настоящее имя (например, t), которое будет использоваться человеком. Можно дать ему простое имя (например, aCustomer) и скрыть его абстракцию, если она очевидна из контекста. Однако зачастую настоящее имя объекта известно только компьютеру, который с ним работает. В таких случаях появляется анонимный объект (например, : Multimedia: :AudioStream). Каждое появление анонимного объекта считается отличным от всех других его появлений. Если вы не знаете связанную с объектом абстракцию, то должны дать ему явное имя (например, agent :).

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

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




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


Рис. 16.2 Простые и составные имена

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




У каждого компонента должно быть имя, отличающее его от других компонентов. Имя - это текстовая строка; взятое само по себе, оно называется простым. К составному имени спереди добавлено имя пакета, в котором находится компонент. Имя компонента должно быть уникальным внутри объемлющего пакета (см. главу 12). Обычно при изображении компонента указывают только его имя, как видно из рис. 25.2. Тем не менее, как и в случае с классами, вы можете снабжать компоненты помеченными значениями или дополнительными разделами (см. главу 6), чтобы показать детали.


Рис. 25.2 Простое и расширенное изображение компонентов

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




Каждый узел должен иметь имя, отличающее его от прочих узлов. Имя - это текстовая строка. Взятое само по себе, оно называется простым именем. Составное имя - это имя узла, к которому спереди добавлено имя пакета, в котором он находится. Имя узла должно быть уникальным внутри объемлющего пакета (см. главу 12). Обычно при изображении узла указывают только его имя, как видно из рис. 26.2. Но, как и в случае с классами, вы можете снабжать узлы помеченными значениями или дополнительными разделами, чтобы показать детали.


Рис. 26.2 Простое и расширенное изображение узлов

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




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

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



Импорт и экспорт


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

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

Итак, допустим, что класс А расположен в одном пакете, а класс В - в другом, причем оба пакета равноправны. Допустим также, что как А, так и В объявлены открытыми частями в своих пакетах. Эта ситуация коренным образом отличается от двух предыдущих. Хотя оба класса объявлены открытыми, свободный доступ одного из них к другому невозможен, ибо границы пакетов непрозрачны. Однако если пакет, содержащий класс А, импортирует пакет-владелец класса в, то А сможет "видеть" В, хотя В по-прежнему не будет "видеть" А. Импорт дает элементам одного пакета односторонний доступ к элементам другого. На языке UML отношения импорта моделируют как зависимости (см. главу 5), дополненные стереотипом import (о механизмах расширения UML см. главу 6). Упаковывая абстракции в семантически осмысленные блоки и контролируя доступ к ним с помощью импорта, вы можете управлять сложностью систем, насчитывающих множество абстракций.

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

Открытые элементы пакета называются экспортируемыми. Так, на рис. 12.4 пакет GUI экспортирует два класса - Windows и Form. Класс EventHandler является защищенной частью пакета. Довольно часто экспортируемыми элементами пакета оказываются интерфейсы (см. главу H).


Рис. 12.4 Импорт и экспорт

Экспортируемые элементы будут видны только тем пакетам, которые явно импортируют данный. В примере на рис. 12.4 пакет Policies импортирует GUI. Таким образом, классы GUI: : Window и GUI: : Form будут из него видны. Но класс GUI: : EventHandler виден не будет, так как является защищенным. С другой стороны, пакет Server не импортирует GUI, и потому элементы Server не имеют права доступа к элементам GUI. По той же причине у GUI нет доступа к содержимому пакета Server.

Зависимости импорта и доступа не являются транзитивными. В данном примере пакет Client импортирует Policies, a Policies - GUI, однако это не значит, что Client импортирует GUI. Таким образом, доступ из пакета Client к экспортируемым частям пакета Policies разрешен, а к экспортируемым частям пакета GUI - нет. Чтобы получить такой доступ, надо явно указать, что Client импортирует GUI.

Примечание: Элемент, видимый внутри пакета, будет видим также и внутри всех вложенных в него пакетов. Вообще, вложенные пакеты могут "видеть"- все, что "видит" объемлющий их пакет.


Интерфейс прикладного программирования


Если вы - разработчик, собирающий систему из компонентов, то вам часто нужно знать интерфейсы прикладного программирования (API - Application Programming Interface), которыми можно пользоваться для "склейки" разных частей. API - это способы стыковки различных программ в системе, моделируемые с помощью интерфейсов и компонентов.

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

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

Моделирование API производится так:

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

На рис. 25.7 раскрыты API исполняемой программы, фигурирующей на двух предыдущих рисунках. Вы видите четыре интерфейса: IApplication, IModels, IRendering и IScripts.


Рис. 25.7 Моделирование API



Исходный код


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

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

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

Моделирование исходного кода осуществляется следующим образом:

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


Например, на рис. 25. 8 показаны некоторые исходные файлы, из которых строится библиотека render.dll. Вы видите четыре заголовочных файла (render.h, rengine.h, poly.h и colortab.h), представляющих спецификации некоторых классов, а также файл render. cpp, представляющий собой реализацию одного из этих классов.


Рис. 25.8 Моделирование исходного кода

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

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


Исключения


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

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

Моделирование исключений производится так:

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

На рис. 20.7 представлена модель иерархии исключений, которые могут возбуждаться контейнерными классами из стандартной библиотеки, например шаблоном (см. главу 9) класса Set. В корне этой иерархии находится абстрактный сигнал Exception, а ниже расположены специализированные исключения: Duplicate, Overflow и Underflow. Как видно, операция add возбуждает исключения Duplicate и Overflow, а операция remove - только исключение Underflow. Вместо этого можно было бы убрать зависимости с переднего плана, перечислив возбуждаемые исключения в спецификации каждой операции. Как бы то ни было, зная, какие исключения может возбудить операция, вы сможете успешно создавать программы, использующие класс Set.


Рис. 20.7 Моделирование исключений



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


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

Выпуск версий более сложных приложений уже представляет определенные трудности. Кроме главной исполняемой программы (обычно ехе-файл) нужен ряд вспомогательных модулей, таких как библиотеки (обычно dll-файлы при работе в контексте СОМ+ или class- либо jar-файлы при работе с языком Java), файлы оперативной справки и файлы ресурсов. Для распределенных систем, вероятно, потребуется несколько исполняемых программ и других файлов, разбросанных по разным узлам. Если вы работаете с системой приложений, то может оказаться так, что одни компоненты уникальны, а другие используются в нескольких приложениях. По мере развития системы управление конфигурацией множества компонентов становится важной задачей, и сложность ее растет, поскольку изменение некоторых компонентов в одном приложении может сказаться на работе других.

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

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

Моделирование исполняемой версии осуществляется так:

Идентифицируйте набор компонентов, который вы хотите моделировать. Как правило, сюда войдут все или некоторые из компонентов, размещенные в одном узле, или же распределение таких наборов компонентов по всем узлам системы. Примите во внимание стереотип каждого компонента в наборе.
В большинстве систем имеется лишь небольшое число различных видов компонентов (таких, как исполняемые программы, библиотеки, таблицы, файлы и документы). Для визуального представления этих стереотипов можно воспользоваться механизмами расширения UML (см. главу 6). Рассмотрите связи каждого компонента с соседями. Чаще всего это предполагает интерфейсы (см. главу 11), которые экспортируют (реализуют) одни компоненты и импортируют (используют) другие. Если вы хотите раскрыть стыковочные узлы системы, смоделируйте эти интерфейсы явно; если же не обходимо представить модель на более высоком уровне абстракции, скройте такие связи, показав лишь зависимости между компонентами.

В качестве примера на рис. 29.3 представлена модель части исполняемой версии автономного робота. Основное внимание обращено на компоненты развертывания, ассоциированные с функциями приводов робота и вычислительными операциями. Вы видите один компонент (driver.dll), который экспортирует интерфейс (IDrive), импортируемый другим компонентом (path.dll). driver.dll экспортирует еще один интерфейс (ISelfTest), который, по всей видимости, используется другими компонентами в системе, хотя они здесь и не показаны. На диаграмме присутствует еще один компонент (collision.dll), который также экспортирует набор интерфейсов, хотя их детали скрыты: показана лишь зависимость от path.dll к collision.dll.




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


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


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

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

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

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

Например, на рис. 25.5 показан набор компонентов, входящий в состав инструментальной программы, работающей на одном персональном компьютере. Мы видим одну исполняемую программу (animator. exe с помеченным значением, обозначающим номер версии) и четыре библиотеки (dlog.dll, wrfrme.dll, render.dll и raytrce.dll). Для изображения компонентов использованы стандартные элементы (см. "Приложение В") для исполняемых программ и библиотек. На диаграмме представлены также зависимости между компонентами.


Рис. 25.5 Моделирование исполняемых программ и библиотек

Примечание: Непосредственный показ зависимости между двумя компонентами - это на самом деле свернутая форма представления реальных отношений между ними. Компонент редко зависит от другого компонента напрямую; чаще он импортирует один или несколько интерфейсов, экспортируемых вторым компонентом. Можно было бы, к примеру, изменить представленную на рис. 25.5 диаграмму, явно указав интерфейсы, которые реализует (экспортирует) render.dll и использует (импортирует) animator.exe. Но для простоты эти детали можно скрыть, показав лишь, что существует зависимость между компонентами.

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

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


Источники дополнительной информации


Самую свежую информацию, касающуюся UML, включая его формальную спецификацию, можно найти в Internet на сайтах http://www.rational.com и http:// www.omg.org. С результатами работы Группы по усовершенствованию можно ознакомиться по адресу http://uml.shl.com.

В Internet существует несколько форумов, где обсуждаются различные вопросы, связанные с UML. Это группы новостей comp.software-eng и comp.object, а также списки рассылки otug@rational.com и uml-rtf@omg.org.

Грейди Буч

Лейквуд, Колорадо, сентябрь 1998 г.

egb@rational.com



Итерации


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



Язык UML Руководство пользователя


Перевод с английского.


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



Экземпляры-прототипы


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

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

Моделирование экземпляров-прототипов осуществляется следующим образом:

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

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


Рис. 13.7 Моделирование экземпляров-прототипов

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



Элементы, принадлежащие пакету


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

Пакет определяет свое пространство имен; это значит, что элементы одного вида должны иметь имена, уникальные в контексте объемлющего пакета. Например, в одном пакете не может быть двух классов Queue, но может быть один класс Queue в пакете Р1, а другой - в пакете Р2. Р1: : Queue и Р2 : : Queue имеют разные составные имена и поэтому являются различными классами.

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

Пакету могут принадлежать другие пакеты, а значит, позволительно осуществить иерархическую декомпозицию модели. Например, может существовать класс Camera, принадлежащий пакету Vision, который, в свою очередь, содержится в пакете Sensors. Составное имя этого класса будет Sensors: : Vis ion: : Camera. Лучше, однако, избегать слишком глубокой вложенности пакетов - два-три уровня являются пределом. В случае необходимости для организации пакетов стоит использовать импортирование (см. ниже), а не вложенность.

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

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


Рис. 12.3 Элементы, принадлежащие пакету

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



Как работать с этой книгой


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

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



Как разобраться в интерфейсе


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

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

На языке UML вы можете связать с интерфейсом значительно больше информации, чтобы сделать его понятнее. Прежде всего, можно описать предусловия и постусловия для каждой операции, а также инварианты для класса или компонента в целом (см. главу 9). Таким образом, клиент сумеет понять, что и как делает интерфейс, без необходимости изучать его реализацию. Если вы хотите формально описать семантику, воспользуйтесь языком ОСL (см. главу 6). Кроме того, к интерфейсу можно присоединить автомат (см. главу 21) и использовать его для специфицирования корректной частичной упорядоченности операций. Наконец, с ним допустимо связать кооперации (см. главу 27), описывающие ожидаемое поведение интерфейса с помощью последовательности диаграмм взаимодействия.



Каркасы


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

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

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

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


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



Классификаторы


В процессе моделирования вы сталкиваетесь с абстракциями сущностей, принадлежащих реальному миру, и сущностей, введенных вами для решения задачи (см. главу 4). Например, при создании системы заказов, работающей через сеть Internet, вам, скорее всего, придется включить в проект класс Клиент (описывающий заказчика) и класс Транзакция (артефакт реализации, соответствующий атомарному действию). В детализированной системе, вероятно, будет еще компонент Цены, экземпляры которого размещаются на всех клиентских узлах. Каждая из перечисленных абстракций фигурирует в модели вместе со своими конкретными экземплярами, поэтому разделение логики и экземпляров сущностей является важной частью моделирования. (Дихотомия "класс/объект" обсуждается в главе 2.)

Некоторые сущности в UML не имеют экземпляров (см. главу 13). К их числу относятся, например, пакеты (см. главу 12) и отношения обобщения (см. главы 5 и 10). В общем смысле те элементы моделирования, которые могут иметь экземпляры, называются классификаторами (ассоциации, рассматриваемые в главах 5 и 10, и сообщения, обсуждаемые в главе 15, также могут иметь экземпляры, но они отличаются от экземпляров классов). Еще важнее то, что классификаторы характеризуются структурными (в форме атрибутов) и поведенческими (в форме операций) свойствами. Все экземпляры одного классификатора обладают общим рядом свойств.

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

интерфейс - набор операций, используемых для того, чтобы специфицировать услуги, предоставляемые классом или компонентом (см. главу 11); тип данных - тип, значения которого не индивидуализированы; сюда входят примитивные встроенные типы, такие как числа и строки, а также перечислимые типы, например Boolean (см. главу 4); сигнал - спецификация асинхронного стимула, используемого для связи между экземплярами (см.
главу 20); компонент - физическая замещаемая часть системы, соответствующая спецификации набора интерфейсов и обеспечивающая их реализацию (см. главу 25); узел - физический элемент, который существует во время выполнения приложения и представляет собой вычислительный ресурс; обычно он обладает, как минимум, некоторой памятью, а иногда и способностью к обработке данных (см. главу 26); прецедент, или вариант использования, - описание совокупности последовательностей действий (в том числе вариантных), выполняемых системой, которые вызывают наблюдаемое изменение, представляющее интерес для конкретного актера (см. главу 16); подсистема - совокупность элементов, из которых отдельные составляют спецификацию поведения других элементов (см. главу 31).

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

Графические изображения классификаторов представлены на рис. 9.2.


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

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


Классы и события


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

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

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

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

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



Клиент-серверная система


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

Существует множество вариаций этой темы. Например, можно выбрать "тонкого" клиента, вычислительные мощности которого ограничены и который, следовательно, занят в основном взаимодействием с пользователем и отображением информации. У "тонких" клиентов может даже не быть собственных компонентов; вместо этого они загружают компоненты с сервера по мере необходимости, как, скажем, в случае с Enterprise JavaBeans. C другой стороны, можно выбрать и "толстого" клиента, у которого вычислительных ресурсов больше и который вследствие этого может заниматься не только визуализацией. Выбор между "тонким" и "толстым" клиентом - это архитектурное решение, на которое влияют различные технические, экономические и политические факторы.

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

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

Моделирование клиент-серверной системы осуществляется так:

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

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





Ключевые абстракции


На языке Java апплет для вывода на экран Web-браузера фразы "Здравствуй, мир!" не представляет собой ничего сложного:

import java.awt.Graphics; class HelloWorld extends Java.applet.Applet { public void paint (Graphics g) { g.drawstring("Здравствуй, мир!", 10, 10); } }

Первая строка кода

import java.awt.Graphics;

делает класс Graphics доступным программе. Префикс java.awt определяет конкретный пакет Java, в котором содержится этот класс. Вторая строка

class HelloWorld extends Java.applet.Applet {

определяет новый класс HelloWorld и указывает, что он является разновидностью класса Applet, находящегося в пакете java . applet.

В следующих трех строках

public void paint (Graphics g) { g.drawstring("Здравствуй, мир!", 10, 10); }

объявляется операция с именем paint, при реализации которой вызывается другая операция, называемая drawstring, ответственная за вывод фразы "Здравствуй , мир!" в указанное место на экране. В полном соответствии с принятым в объектно-ориентированном программировании стиле drawString - это операция над параметром с именем g, типом которого является класс Graphics.

Моделирование этого приложения на языке UML не вызывает затруднений. Как видно из рис. 3.1, класс HelloWorld графически можно представить пиктограммой прямоугольника (информация о классах приводится в главах 4 и 9). Здесь же показана операция paint; ее формальные параметры скрыты, а реализация специфицирована в примечании.


Рис. 3.1 Ключевые абстракции класса HelloWorld

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


На этой диаграмме классов представлена суть приложения "Здравствуй, мир!", но многие детали опущены. Как видно из приведенного выше кода, в профамме задействованы два других класса - Applet и Graphics - и каждый по-разному. Applet является родителем HelloWorld, а класс Graphics необходим для реализации одной из его операций - paint. Эти классы и их отношения с классом HelloWorld можно изобразить в виде другой диаграммы классов (см. рис. 3.2).


Рис. 3.2 . Непосредственные соседи класса HelloWorld

Классы Applet и Graphics показаны в виде прямоугольных пиктограмм. Операции не представлены и на пиктограммах скрыты. Незакрашенная стрелка, направленная от класса HelloWorld к классу Applet, соответствует отношению обобщения; в данном случае это означает, что HelloWorld является потомком Applet. Пунктирная стрелка от класса HelloWorld к классу Graphics означает отношение зависимости (см. главы 5 и 10), поскольку HelloWorld использует класс Graphics.

Это, однако, еще не завершение структуры, лежащей в основе класса Hello-World. Изучив библиотеки языка Java, содержащие классы Applet и Graphics, вы обнаружите, что оба они являются частью более обширной иерархии. На рис. 3.3 показана диаграмма классов, которые расширяет и реализует класс HelloWorld.


Рис. 3.3 Иерархия наследования класса HelloWorld

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

Из рисунка видно, что HelloWorld - всего лишь листовой узел большой иерархии классов. Он выступает в качестве потомка класса Applet, который, в свою очередь, является потомком класса Panel, и так далее вплоть до Object -родителя всех классов в языке Java. Таким образом, данная модель соответствует библиотеке Java - у каждого класса есть родитель.

Отношение между элементами ImageObserver и Component отличается от остальных, и диаграмма классов передает это отличие.


В библиотеке Java ImageObserver является интерфейсом (см. главу 11). Это означает, в частности, что у него нет реализации, а потому необходимо, чтобы его реализовывали другие классы. Как видно из рисунка, интерфейсы в UML обозначаются кружочком. О том, что класс Component реализует этот интерфейс, свидетельствует линия, направленная от реализации (Component) к интерфейсу (ImageObserver).

Итак, Не11oWorld непосредственно сотрудничает только с двумя классами (Applet и Graphics), но они являются всего лишь малой частью огромной библиотеки предопределенных классов Java. Для облегчения работы ее интерфейсы и классы организованы в виде нескольких пакетов. Корневой пакет среды разработки Java назван, как и следовало ожидать, java. Внутри него содержится несколько других пакетов, а внутри них - еще несколько пакетов, классы и интерфейсы. Класс Object заключен внутри пакета lang, так что его полное имя будет Java. lang.Object. Аналогично классы Panel, Container и Component содержатся в пакете awt, а класс Applet - в пакете applet. Интерфейс ImageObserver входит в состав пакета image, который, в свою очередь, находится в пакете awt, поэтому полное имя интерфейса будет выражено в виде довольно длинной строки j ava.awt.image.ImageObserver.

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


Рис. 3.4 Организация в пакеты классов, участвующих в приложении HelloWorld

Пакеты на языке UML изображают в виде папок с закладками (см. главу 12). Пакеты могут быть вложенными; существующие между ними зависимости показаны с помощью пунктирных стрелок. Например, класс HelloWorld зависит от пакета java.applet, a java.applet - от java.awt.


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


Как правило, примечания используются для того, чтобы в свободной форме записывать наблюдения, обзоры и пояснения. Включая такие комментарии в модель, вы можете превратить ее в хранилище всевозможных артефактов, создаваемых в процессе разработки. Кроме того, посредством комментариев можно визуализировать требования к системе и явно показать, как они связаны с теми или иными частями модели.
Моделирование комментария производится следующим образом:
Включите его в виде текста в примечание и расположите рядом с элементом, к которому он относится. Можно показать отношение более явно, соединив примечание с соответствующими элементами при помощи зависимости. Помните, что элементы модели можно скрыть или сделать видимыми. Это означает, что не обязательно делать комментарий видимым всегда, когда видны элементы, к которым он присоединен. Лучше раскрывать комментарии в диаграммах только тогда, когда это необходимо для передачи какой-либо информации. Если комментарий длинный или содержит нечто более сложное, чем обычный текст, подумайте о том, чтобы вынести его во внешний документ, на который можно поставить ссылку, или встроить его в примечание, присоединенное к модели. По мере продвижения процесса моделирования убирайте все комментарии, кроме тех, что содержат важные решения, которые не могут быть выведены из самой модели, либо тех, что представляют исторический интерес.
В качестве примера на рис. 6.8 показана промежуточная модель, возникшая в процессе работы над иерархией классов. Здесь отражены требования к модели наряду с некоторыми примечаниями, касающимися пересмотра проекта. (Вам может пригодиться материал глав 5 и 10, где обсуждаются, соответственно, простые обобщения и более сложные их формы.)

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

Коммуникация


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

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

Во-вторых, сообщение может быть передано от одного активного объекта другому активному. Здесь мы имеем случай межпроцессной коммуникации, которая может осуществляться двумя способами. В первом варианте некоторый активный объект может синхронно вызывать операцию другого (о событиях сигналов и вызовов см. главу 20). Такой способ имеет семантику рандеву (Rendezvous): вызывающий объект затребует выполнение операции и ждет, пока принимающая сторона получит вызов, выполнит операцию и вернет некоторый объект (если есть что возвращать); затем оба объекта продолжают работать независимо друг от друга. В течение всего времени выполнения вызова оба потока управления будут блокированы. Во втором варианте один активный объект может асинхронно послать сигнал другому или вызвать его операцию. Семантика такого способа напоминает почтовый ящик (Mailbox): вызывающая сторона посылает сигнал или вызывает операцию, после чего продолжает выполняться. Тем временем получающая сторона принимает сигнал или вызов, как только будет к этому готова. Пока она обрабатывает запрос, все вновь поступающие события или вызовы ставятся в очередь. Отреагировав на запрос, принимающий объект продолжает свою работу. Семантика почтового ящика проявляется в том, что оба объекта не синхронизированы, просто один оставляет сообщение для другого.

В UML синхронное сообщение изображается полной стрелкой, а асинхронное - "полустрелкой" (см. рис. 22.2).


Рис. 22.2 Коммуникация

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

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

Примечание: С помощью ограничений (см. главу 6) можно моделировать различные вариации посылки синхронных и асинхронных сообщений. Например, для моделирования отложенного рандеву (Balking rendezvous), имеющегося в языке Ada, можно воспользоваться синхронным сообщением с ограничением вида {wait = 0}, которое говорит о том, что вызывающий объект не будет дожидаться получателя. Можно смоделировать и тайм-аут с помощью ограничения вида {wait = 1 ms}, которое говорит, что вызывающий объект будет ждать приема сообщения получателем не более одной миллисекунды.


Компоненты


Программа "Здравствуй, мир!" реализована в виде апплета и поэтому не запускается самостоятельно, а существует как часть Web-страницы. Приложение начинает выполняться, только когда открывается содержащая его страница: запуск осуществляет механизм браузера, активизирующий выполнение объекта Thread этого апплета. Однако частью Web-страницы является не сам класс HelloWorld, а его двоичная форма, создаваемая компилятором Java, который преобразует исходный код в выполняемый компонент. Это позволяет взглянуть на систему совершенно с другой стороны. На всех предшествующих диаграммах мы видели логическое представление апплета, теперь же посмотрим на приложение как на набор физических компонентов (см. главу 25). Такой взгляд на систему представляет диаграмма компонентов (см. рис. 3.6).


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

Каждая из показанных на этом рисунке пиктограмм соответствует элементу UML в представлении системы с точки зрения реализации. Компонент hello.java - это исходный код логического класса HelloWorld, то есть файл, которым можно манипулировать из среды разработки и с помощью инструментальных средств по управлению конфигурацией системы. Исходный код преобразуется в двоичный файл hellо.class компилятором Java, после чего может быть выполнен виртуальной машиной Java, установленной на компьютере.

Каноническая пиктограмма компонента - прямоугольник с двумя вкладками. Двоичный апплет HelloWorld.class является разновидностью этого символа, но границы прямоугольника толще, благодаря чему можно понять, что это исполняемый компонент (аналогично активному классу). Компоненту hello.java присвоена определенная пользователем пиктограмма, которая представляет текстовый файл. Аналогично и пиктограмма Web-страницы hello.html получена путем расширения нотации UML. Как видно из рисунка, в Web-страницу входит еще один компонент hello.jpg, также изображенный при помощи пользовательской пиктограммы, которая в данном случае содержит схематическое изображение рисунка. Так как последние три компонента представлены графическими символами, определенными пользователем, их имена расположены снаружи пиктограмм. (Механизмы расширения UML рассматриваются в главе 6.)

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

[Предыдущая глава]

[Содержание]

[Следующая глава]



Компоненты и интерфейсы


Интерфейс (см. главу 11) - это набор операций, которые описывают услуги, предоставляемые классом или компонентом. Существенным является отношение между компонентом и интерфейсом. Все популярные компонентные средства операционных систем (такие как СОМ+, CORBA и Enterprise JavaBeans) используют интерфейсы для "склеивания" различных компонентов.

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

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


Рис. 25.4 Компоненты и интерфейсы

Интерфейс, реализуемый компонентом, называется экспортируемым интерфейсом (Export interface). Это означает, что компонент через данный интерфейс предоставляет ряд услуг другим компонентам. Компонент может экспортировать много интерфейсов. Интерфейс, которым компонент пользуется, называется импортируемым (Import interface). Это означает, что компонент совместим с таким интерфейсом и зависит от него при выполнении своих функций. Компонент может импортировать различные интерфейсы, причем ему разрешается одновременно экспортировать и импортировать интерфейсы.

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

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



Компоненты и классы


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

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

Первое отличие самое важное. При моделировании системы решение о том, что использовать - класс или компонент, - очевидно: если моделируемая сущность непосредственно размещается в узле (см. главу 26), то это компонент, в противном случае - класс.

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


Рис. 25.3 Компоненты и классы

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

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



Концептуальная модель UML


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



Конкретные экземпляры


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

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

Моделирование конкретных экземпляров осуществляется так:

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

В качестве примера на рис. 13.6 показана диаграмма объектов, взятая из системы проверки подлинности кредитных карточек. Такой ее можно увидеть в отладчике, тестирующем работающее приложение. На рисунке показан один мульти-объект, содержащий анонимные экземпляры класса Transact ion. Присутствуют также два явно поименованных объекта - primaryAgent и current, в описании которых показан класс, хотя и по-разному. На диаграмме явно показано и текущее состояние объекта primaryAgent.


Рис. 13.6 Моделирование конкретных экземпляров

Обратите внимание на использование зависимости со стереотипом instanceOf, указывающей на класс primaryAgent. Обычно такие отношения "класс/объект" показывают явным образом, только если собираются показать и отношения с другими классами.



Контекст


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

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

Взаимодействия между объектами встречаются и при реализации операций (см. главы 4 и 9). Параметры операции, любые локальные для нее переменные и глобальные объекты, видимые внутри операции, могут взаимодействовать друг с другом для выполнения реализуемого ею алгоритма (о моделировании операций подробно рассказывается в главах 19 и 27). Так, вызов операции moveToPo-sition (p : Position), определенной в классе мобильного робота, предполагает взаимодействие между параметром (p), глобальным для операции объектом (currentPosition) и, возможно, несколькими локальными объектами (скажем, локальными переменными, используемыми внутри операции для вычисления промежуточных точек на пути робота к новому местоположению).

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

Примечание: Взаимодействия могут входить и в представление компонента (см. главу 25), узла (см. главу 26) или прецедента (см. главу 16), которые в UML являются разновидностями классификаторов (см. главу 9). В контексте прецедентов взаимодействие являет собой сценарий, который, в свою очередь, представляет один из потоков деятельности прецедента (о моделировании реализации прецедента рассказывается в главе 27).


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

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

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

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



Контекст системы


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

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

Моделирование контекста системы состоит из следующих шагов:

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

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


Рис. 17.2 Моделирование контекста системы

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



Краткая история UML


Объектно-ориентированные языки моделирования появились в период с середины 70-х до конца 80-х годов, когда исследователи, поставленные перед необходимостью учитывать новые возможности объектно-ориентированных языков программирования и требования, предъявляемые все более сложными приложениями, вынуждены были начать разработку различных альтернативных подходов к анализу и проектированию. С 1989 по 1994 год число различных объектно-ориентированных методов возросло с десяти более чем до пятидесяти. Тем не менее многие пользователи испытывали затруднения при выборе языка моделирования, который бы полностью соответствовал их потребностям, что послужило причиной так называемой "войны методов". В результате этих войн появилось новое поколение методов, среди которых особое значение приобрели языки Booch, созданный Грейди Бучем (Grady Booch), OOSE (Object-Oriented Software Engineering), разработанный Айваром Джекобсоном (Ivar Jacobson) и ОМТ (Object Modeling Technique), автором которого является Джеймс Рамбо (James Rumbaugh). Кроме того, следует упомянуть языки Fusion, Шлаера-Меллора (Shlaer-Mellor) и Коада-Йордона (Coad-Yourdon). Каждый из этих методов можно считать вполне целостным и законченным, хотя любой из них имеет не только сильные, но и слабые стороны. Выразительные возможности метода Буча особенно важны на этапах проектирования и конструирования модели. OOSE великолепно приспособлен для анализа и формулирования требований, а также для высокоуровневого проектирования. ОМТ-2 оказался особенно полезным для анализа и разработки информационных систем, ориентированных на обработку больших объемов данных.

Критическая масса новых идей начала формироваться к середине 90-х годов, когда Грейди Буч (компания Rational Software Corporation), Айвар Джекобсон (Objectory) и Джеймс Рамбо (General Electric) предприняли попытку объединить свои методы, уже получившие мировое признание как наиболее перспективные в данной области. Являясь основными авторами языков Booch, OOSE и ОМТ, партнеры попытались создать новый, унифицированный язык моделирования и руководствовались при этом тремя соображениями.
Во-первых, все три метода, независимо от желания разработчиков, уже развивались во встречном направлении. Разумно было продолжать эту эволюцию вместе, а не по отдельности, что помогло бы в будущем устранить нежелательные различия и, как следствие, неудобства для пользователей. Во-вторых, унифицировав методы, проще было привнести стабильность на рынок инструментов объектно-ориентированного моделирования, что дало бы возможность положить в основу всех проектов единый зрелый язык, а создателям инструментальных средств позволило бы сосредоточиться на более продуктивной деятельности. Наконец, следовало полагать, что подобное сотрудничество приведет к усовершенствованию всех трех методов и обеспечит решение задач, для которых любой из них, взятый в отдельности, был не слишком пригоден.

Начав унификацию, авторы поставили перед собой три главные цели:

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

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


Создавая UML, разработчики старались найти оптимальное решение этих проблем.

Официально создание UML началось в октябре 1994 года, когда Рамбо перешел в компанию Rational Software, где работал Буч. Первоначальной целью было объединение методов Буча и ОМТ. Первая пробная версия 0.8 Унифицированного Метода (Unified Method), как его тогда называли, появилась в октябре 1995 года. Приблизительно в это же время в компанию Rational перешел Джекобсон, и проект UML был расширен с целью включить в него язык OOSE. В результате совместных усилий в июне 1996 года вышла версия 0.9 языка UML. На протяжении всего года создатели занимались сбором отзывов от основных компаний, работающих в области конструирования программного обеспечения. За это время стало ясно, что большинство таких компаний сочло UML языком, имеющим стратегическое значение для их бизнеса. В результате был основан консорциум UML, в который вошли организации, изъявившие желание предоставить ресурсы для работы, направленной на создание полного определения UML.

Версия 1.0 языка появилась в результате совместных усилий компаний Digital Equipment Corporation, Hewlett Packard, I-Logix, Intellicprp, IBM, ICON Computing, MCI Systemhouse, Microsoft, Oracle, Rational, Texas Instruments и Unisys. UML 1.0 оказался хорошо определенным, выразительным, мощным языком, применимым для решения большого количества разнообразных задач. В январе 1997 года он был представлен Группе по управлению объектами (Object Management Group, OMG) на конкурс по созданию стандартного языка моделирования.

Между январем и июнем 1997 года консорциум UML расширился, в него вошли практически все компании, откликнувшиеся на призыв OMG, а именно: Andersen Consulting, Ericsson, ObjecTime Limited, Platinum Technology, Ptech, Reich Technologies, Softeam, Sterling Software и Taskon. Чтобы формализовать спецификации UML и координировать работу с другими группами, занимающимися стандартизацией, под руководством Криса Кобрина (Cris Kobryn) из компании MCI Systemhouse и Эда Эйкхолта (Ed Eykholt) из Rational была организована семантическая группа.Пересмотренная версия UML (1.1) была снова представлена на рассмотрение OMG в июле 1997 года. В сентябре версия была утверждена на заседаниях Группы по анализу и проектированию и Комитета по архитектуре OMG, a 14 ноября 1997 года принята в качестве стандарта на общем собрании всех членов OMG.

Дальнейшая работа по развитию UML проводилась Группой по усовершенствованию (Revision Task Force, RTF) OMG под руководством Криса Кобрина. В июне 1998 года вышла версия UML 1.2, а осенью 1998 - UML 1.3. Данное руководство пользователя описывает именно эту версию языка.


Кратность


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

нет ни одного экземпляра - тогда класс становится служебным (Utility), содержащим только атрибуты и операции с областью действия класса; D ровно один экземпляр - такой класс называют синглетным (Singleton); заданное число экземпляров; произвольное число экземпляров - вариант по умолчанию.

Количество экземпляров класса называется его кратностью. В общем смысле кратность - это диапазон возможных кардинальных чисел некоторой сущности (кратность встречается также у ассоциаций, см. главы 5 и 10). В языке UML кратность класса задается выражением, написанным в правом верхнем углу его пиктограммы. Например, как показано на рис. 9.6, класс NetworkController является синглетным, а у класса ControlRod имеется ровно три экземпляра.


Рис. 9.6 Кратность

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



Логическая схема базы данных


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

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

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

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


Примечание: Рассмотрение логического проектирования базы данных выходит за рамки данной книги, цель которой состоит только в том, чтобы показать, как моделировать схемы с помощью языка UML. На практике все сводится к применению стереотипов (см. главу 6), настроенных на конкретный тип используемой базы данных (реляционной или объектно-ориентированной).

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


Рис. 8.3 Моделирование схемы

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

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

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


Механизм


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

Механизмы (Mechanisms) - это автономные кооперации; их контекстом является не какой-то один прецедент или операция, но система в целом. Любой элемент, видимый в некоторой части системы, является кандидатом на участие в механизме.

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

Моделирование механизмов осуществляется следующим образом:

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



Механизмы


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

Как можно понять из библиотеки языка Java, операция paint наследуется от класса Component. Но все еще остается открытым вопрос, как происходит ее вызов. Ответ состоит в том, что paint вызывается в контексте нити (см. главу 22), в которой работает весь апплет, как показано на рис. 3.5.


Рис. 3.5 . Механизм изображения

На рисунке представлена кооперация нескольких объектов, включая один экземпляр класса HelloWorld. Другие объекты являются частью рабочей среды Java и в основном остаются на заднем плане создаваемых вами апплетов. В UML экземпляры (см. главу 11) изображаются в точности как классы, но, в отличие от последних, с подчеркнутыми именами. Первые три объекта на диаграмме являются анонимными, то есть не имеют уникального имени. Объекту HelloWorld принадлежит имя (target), известное объекту ComponentPeer.

Порядок событий можно моделировать с помощью диаграммы последовательностей (см. главу 18), представленной на рис. 3.5. Последовательность начинается с запуска объекта Thread, который вызывает операцию run объекта Toolkit. Объект Toolkit обращается затем к одной из своих собственных операций (callbackLoop), которая, в свою очередь, вызывает операцию handleExpose объекта ComponentPeer. Только после этого ComponentPeer обращается к операции paint целевого объекта. ComponentPeer предполагает, что целевой объект является экземпляром класса Component, но в данном случае мы фактически имеем дело с его потомком (а именно HelloWorld), так что полиморфно вызывается операция paint класса HelloWorld.




Механизм - это просто другое название образца проектирования, когда он применяется к сообществу классов. Например, типичная проблема проектирования, с который сталкивается программист, пишущий на языке Java, - как видоизменить класс, который умеет отвечать на некоторое множество событий, таким образом, чтобы он отвечал на события иного рода, не трогая кода исходного класса. Типичным решением этой проблемы является адаптер (Adaptor pattern) - структурный образец проектирования для преобразования одного интерфейса в другой. Этот образец является настолько общим, что имеет смысл дать ему название, а затем использовать в моделях всякий раз, как возникает аналогичная проблема.

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

Во-первых, как показано на рис. 28.1, механизм просто именует множество абстракций, которые совместно работают для обеспечения типичного поведения, представляющего некий интерес. Такие механизмы моделируются как простые кооперации (см. главу 27), поскольку являются всего лишь именами для сообщества классов. Раскрыв такую кооперацию, можно увидеть ее структурные аспекты (обычно изображаемые в виде диаграмм классов) и поведенческие (обычно изображаемые в виде диаграмм взаимодействия). Кооперации подобного типа захватывают разные абстракции системы; весьма вероятно, что некоторый класс будет членом нескольких коопераций.

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


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



Местоположение


Распределенные системы по своей природе состоят из компонентов, физически разбросанных по разным узлам. Для многих систем местоположение (Location) компонентов (см. главу 25) фиксируется в момент установки системы. Но встречаются и такие системы, в которых компоненты мигрируют с одного узла (см. главу 26) на другой.

В UML вид системы с точки зрения развертывания моделируется с помощью диаграмм развертывания (см. главу 30), описывающих топологию процессоров и устройств, на которых выполняется система. Компоненты, такие как исполняемые модули, библиотеки и таблицы, размещаются в этих узлах. Каждый экземпляр узла владеет собственными экземплярами тех или иных компонентов, а каждый экземпляр компонента принадлежит ровно одному экземпляру узла (хотя различные экземпляры компонента одного вида могут находиться на разных узлах, - о дихотомии "класс/объект" см. главы 2 и 13). Например, на рис. 23.3 показан исполняемый компонент vision.exe, находящийся в узле с именем KioskServer.


Рис. 23.3 Местоположение

В узле могут размещаться и экземпляры простых классов (см. главы 4 и 9). Так, на рис. 23.3 мы видим экземпляр класса LoadAgent в узле Router.

Как видно из рисунка, моделировать положение элемента в UML можно двумя способами. Во-первых, как в случае KioskServer, можно физически поместить элемент (его текстовое или графическое описание) в дополнительный раздел объемлющего узла. Во-вторых - примером является LoadAgent - можно воспользоваться помеченным значением location для обозначения узла, на котором размещен класс (о помеченных значениях и дополнительных разделах см. главу 6). Обычно первый способ применяется, когда важно дать в диаграмме визуальное представление пространственного разделения и группирования компонентов. Второй способ удобнее, когда моделирование положения элемента для данной диаграммы является хотя и важным, но второстепенным, например, когда вы хотите визуализировать передачу сообщений между экземплярами.

Примечание: Второй способ моделирования положения элемента особенно полезен, когда нужно показать изменение местонахождения компонента во времени. Например, можно воспользоваться сообщением become (см. главу 13), чтобы смоделировать объект, который в текущий момент находится в одном месте, но переместится в другое. Аналогично для показа семантических отношений между разнесенными объектами можно применить сообщение copy (см. главу 13).



Межпроцессная коммуникация


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

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

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

Смоделируйте несколько потоков управления. Рассмотрите, какие из этих активных объектов могут быть представлены процессами, а какие - нитями. Чтобы их различить, воспользуйтесь подходящими стереотипами (см. главу 6). Смоделируйте обмен сообщениями с помощью асинхронных, а вызовы удаленных процедур - с помощью синхронных коммуникаций. Неформально опишите используемый механизм с помощью примечаний (см. главу 6) или - более строго - с помощью коопераций (см. главу 27).

На рис. 22.5 показана распределенная система бронирования билетов, в которой процессы исполняются на четырех узлах (см. главу 26). Каждый объект маркирован стереотипом process, а также помеченным значением location, которое определяет его физическое положение. Коммуникации между объектами ReservatibnAgent (АгентБронирования), TicketingManager (ДиспетчерВы-дачиБилетов) и HotelAgent (ГостиничныйАгент) асинхронны. В примечании написано, что коммуникации построены на основе службы сообщений, реализованной на JavaBeans. Коммуникация между объектами TripPlanner (Планиров-щикМаршрута) и ReservationSystem (СистемаРезервирования) синхронная.


Рис. 22.5 Моделирование межпроцессной коммуникации

Семантика их взаимодействия показана в кооперации, названной CORBA ORB. TripPlanner выступает в роли клиента, a ReservationAgent - сервера. Раскрыв эту кооперацию, вы увидите детали совместной работы сервера и клиента.



Мигрирующие объекты


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

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

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

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

Моделирование миграции объектов осуществляется так:


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

На рис. 23.6 приведена диаграмма кооперации (см. главу 18), моделирующая миграцию Web-агента, который перемещается между узлами, собирая информацию и торгуясь за ресурсы с целью автоматически найти минимальную стоимость билета. Точнее, на этой диаграмме показан экземпляр (с именем t) класса ТурАгент, мигрирующий с одного сервера на другой. По пути этот объект взаимодействует с анонимными экземплярами класса Аукционер на каждом узле и в конце концов доставляет результат торгов объекту Маршрут, который расположен на узле Client server.


Рис. 23.6 Моделирование мигрирующих объектов


Модели


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

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

Вид - это одна из проекций модели. В Рациональном Унифицированном Процессе существует пять тесно связанных друг с другом видов системной архитектуры (см. главу 2): с точки зрения проектирования, процессов, развертывания, реализации и прецедентов.



Модели и представления


Модель - это упрощение реального мира; реальность в ней описывается в контексте моделируемой системы. Проще говоря, модель - это абстракция системы.

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

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

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

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

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