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

         

Наполнение


Диаграмма деятельности в общем случае состоит из:

состояний деятельности и состояний действия; переходов (см. главу 21); Q объектов (см. главу 13).

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

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



Непрограммные сущности


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

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

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

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

Как видно из рис. 4.11, абстрагирование людей (AccountsReceivableAgent -АгентПоДебиторскойЗадолженности) и аппаратуры (Robot) в виде классов вполне естественно, поскольку они представляют собой множества объектов с общей структурой и поведением. Сущности, внешние по отношению к системе, часто моделируются как актеры (см. главу 16).


Рис. 4.11 Моделирование непрограммных сущностей



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


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

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

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


Применяйте наиболее подходящую семантику - последовательную, охраняемую или параллельную.

На рис. 22. 4 показана часть вида трейдерской системы с точки зрения процессов. Вы видите три объекта, которые параллельно питают систему информацией: StockTicker (БиржевойТикер), indexWatcher (наблюдательИндекса) и CNNNewsFeed (НовостнаяЛентаСММ), названные соответственно s, i и с. Два из них, s и i, обмениваются каждый со своим экземпляром класса Analyst (Аналитик) - al и а2. В рамках этого небольшого фрагмента модели класс Analyst может быть спроектирован в упрощенном виде - из расчета на то, что в каждый момент времени в любом из его экземпляров может быть активен только один поток управления. Однако оба экземпляра класса Analyst одновременно общаются с объектом AlertManager (ДиспетчерОповещений), которому мы дали имя т. Следовательно, m необходимо спроектировать так, чтобы он сохранял свою семантику в присутствии нескольких потоков управления m и c одновременно общаются с t - объектом класса TradingManager (управляющийТрейдингом). Каждому соединению присвоен порядковый номер, определяемый тем, какой поток управления им владеет.


Рис. 22.4 Моделирование потоков управления

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

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


Новая семантика


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

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

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

В качестве примера на рис. 6.11 представлена модель небольшого фрагмента корпоративной системы управления человеческими ресурсами.


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

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



Новые строительные блоки


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

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

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

Допустим, вы используете диаграммы деятельности для моделирования бизнес-процесса, описывающего действия тренеров и команд во время какого-либо спортивного события. Следует визуально отличать тренеров и команды друг от друга и от иных сущностей предметной области, таких как состязания и занятые командами места. Как явствует из рис. 6.9, две сущности - Тренер (Coach) и Команда (Team) - отличаются от остальных. Это не просто разновидности классов, а новые примитивные строительные блоки, которые можно использовать в данном контексте. Создать такие блоки можно, определив стереотипы coach и team


Рис. 6.9 Моделирование новых строительных блоков

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



Новые свойства


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

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

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

Предположим, например, что вам нужно связать создаваемые модели с системой управления конфигурацией проекта. Помимо всего прочего, это предполагает отслеживание номера версии, текущего статуса версии в хранилище (Check in/check out status) и, возможно, дат создания и модификации каждой подсистемы (см. главу 31). Поскольку вся эта информация специфична для процесса разработки, она не является частью базового UML, хотя ее можно включить в модель с помощью помеченных значений. В частности, эта информация не является просто атрибутом класса. Номер версии подсистемы - это часть метаданных, но не самой модели.

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


Рис. 6.10 Моделирование новых свойств

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



Объектное моделирование


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

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

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

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

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

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

Если вы приняли объектно-ориентированный взгляд на мир, то придется ответить на ряд вопросов (см. главу 2). Какая структура должна быть у хорошей объектно-ориентированной архитектуры? Какие артефакты должны быть созданы в процессе работы над проектом? Кто должен создавать их? И наконец, как оценить результат?

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

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

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

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


Объектные структуры


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

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

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

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

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

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


Рис. 14.2 Моделирование объектных структур

Как видно из рисунка, один из объектов соответствует самому роботу (r, экземпляр класса Robot); в настоящий момент он находится в состоянии moving (движется). Этот объект связан с экземпляром w класса World (Мир), являющегося абстракцией модели мира робота. В свою очередь объект w связан с мультиобъектом, который состоит из экземпляров класса Element, описывающего сущности, опознанные роботом, но еще не включенные в его модель мира. Эти элементы помечены как части глобального состояния робота.

В текущий момент времени экземпляр w связан с двумя экземплярами класса Area. У одного из них (а2) показаны его собственные связи с тремя объектами класса Wall (Стена) и одним - класса Door (Дверь). Указана ширина каждой из трех стен и обозначено, что каждая связана с соседними. Как видно из диаграммы, робот распознал, что замкнутое помещение, в котором он находится, имеет с трех сторон стены, а с четвертой - дверь.


Объекты и роли


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

Примечание: Именно эта особенность отличает кооперации, все объекты которых являются прототипами, играющими определенные роли, а не конкретными объектами реального мира.

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

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



Обязанности


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

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

Графически обязанности изображают в особом разделе в нижней части пиктограммы класса (см. рис. 4.8). Их можно указать также в примечании; подробно о примечаниях рассказывается в главе 6.


Рис. 4.8 Обязанности

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



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


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

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

На рис. 9.4 (упрощенный вариант предыдущего рисунка) показано, что имя свойства, которое имеет область действия classifier, подчеркивается. Если подчеркивание отсутствует, предполагается область действия instance.


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

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



Обобщение


Обобщение (Generalization) - это отношение специализации/обобщения, при котором объекты специализированного элемента (потомка - Child) можно подставить вместо объектов обобщенного элемента (родителя, или предка, - Parent).




Обобщения


Обобщение (Generalization) - это отношение между общей сущностью (суперклассом, или родителем) и ее конкретным воплощением (субклассом, или потомком). Обобщения иногда называют отношениями типа "является", имея в виду, что одна сущность (например, класс BayWindow) является частным выражением другой, более общей (скажем, класса Window). Обобщение означает, что объекты класса-потомка могут использоваться всюду, где встречаются объекты класса-родителя, но не наоборот. Другими словами, потомок может быть подставлен вместо родителя. При этом он наследует свойства родителя, в частности его атрибуты и операции. Часто, хотя и не всегда, у потомков есть и свои собственные атрибуты и операции, помимо тех, что существуют у родителя. Операция потомка с той же сигнатурой, что и у родителя, замещает операцию родителя; это свойство называют полиморфизмом (Polymorphism). Графически отношение обобщения изображается в виде линии с большой незакрашенной стрелкой, направленной на родителя, как показано на рис. 5.3. Применяйте обобщения, когда хотите показать отношения типа "родитель/потомок".


Рис. 5.3 Обобщение

Класс может иметь одного или нескольких родителей или не иметь их вовсе. Класс, у которого нет родителей, но есть потомки, называется базовым (base) или корневым (root), а тот, у которого нет потомков, - листовым (leaf). О классе, у которого есть только один родитель, говорят, что он использует одиночное наследование (Single inheritance); если родителей несколько, речь идет о множественном наследовании (Multiple inheritance).

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

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


Обобщением называется отношение между общей сущностью, которую называют суперклассом или родителем, и более специализированной ее разновидностью, называемой подклассом или потомком (см. главу 5). Например, общий класс Window (Окно) можно специализировать, создав класс-потомок Mu1tiPaneWindow (МногоФорточноеОкно). Благодаря отношениям обобщения от потомка к родителю MultiPaneWindow унаследует структуру и поведение своего родителя - Window. У потомка могут появиться новые элементы структуры или поведения, а унаследованные - модифицироваться. В отношениях обобщения экземпляры потомка везде могут использоваться вместо экземпляров родителя, - это значит, что потомок может замещать родителя. Как правило, достаточно одиночного наследования, когда у класса имеется всего один родитель. Но иногда используют и множественное наследование, которое тоже можно моделировать на языке UML. Так, на рис. 10.2 показано множество классов, взятых из финансового приложения. Как видите, класс Активы имеет трех потомков: БанковскийСчет, Недвижимость и ЦенныеБумаги. У первого и последнего из них имеются собственные потомки. Например, Акция и Облигация являются потомками класса ЦенныеВумаги.


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

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

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


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

Отношения обобщения между пакетами очень похожи на отношения обобщения между классами. Например, как видно из рис. 12.5, пакет GUI содержит два экспортируемых класса (Windows и Form) и один защищенный (EventHandler). Существуют две специализации пакета GUI а именно: WindowsGUI и MacGUI. Они наследуют открытые и защищенные элементы своего родителя. Как и в случае с классами, пакеты могут замещать наследуемые элементы или добавлять новые. Например, пакет WindowsGUI содержит классы GUI: : Windows и GUI: : Event-Handler. Кроме того, в нем переписан класс Form и добавлен новый класс VBForm. Участвующие в обобщении пакеты следуют тому же принципу подстановки, что и классы. Специализированные пакеты (такие, как WindowsGUI) могут использоваться всюду, где допустимо использование их родителей (наподобие GUI).


Рис. 12.5 Отношения обобщения между пакетами



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


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

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

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

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

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



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


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

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

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

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

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

Например, на рис. 28.4 показано использование образца проектирования Команда (см. Gamma et al, "Design Patterns". Reading, Massachusetts: Addison-Wesley, 1995). Документация гласит, что этот образец "инкапсулирует запрос в виде объекта, позволяя тем самым параметризовать клиенты, выдающие разные запросы, ставить запросы в очередь и протоколировать их, а также поддерживать операции, допускающие отмену".
Как видно из модели, этот образец имеет четыре параметра, которые должны быть связаны с элементами в данном контексте. Модель показывает пример такого связывания, где с параметрами образца связаны классы Приложение, КомандаВставки, КомандаОткрытия, ПунктМеню и Документ.




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

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

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

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




На рис. 28.6 показана диаграмма последовательностей (см. главу 18), представляющая поведение этого образца.





Общие механизмы языка UML


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

спецификации (Specifications); дополнения (Adornments); принятые деления (Common divisions); механизмы расширения (Extensibility mechanisms).

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

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

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

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


Рис. 2.16 Дополнения

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

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

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


Рис. 2.17 . Классы и объекты

На этом рисунке показан один класс Customer (Клиент) и три объекта: Jan (явно определенный как объект данного класса), :Customer (анонимный объект класса Customer) и Elyse (спецификация которого относит его к классу Customer, хотя это и не выражено явно).

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

Еще одним вариантом членения является деление на интерфейс и его реализацию. Интерфейс декларирует контракт (см. главу 11), а реализация представляет конкретное воплощение этого контракта и обязуется точно следовать объявленной семантике интерфейса. UML позволяет моделировать обе эти категории, интерфейсы и их реализации, как показано на рис. 2.18: в данном случае один компонент spellingwizard.dll реализует два интерфейса lUnknown и ISpelling.


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


Рис. 2.18 Интерфейсы и реализации

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

стереотипы; помеченные значения; ограничения.

Стереотип (Stereotype) расширяет словарь UML, позволяя на основе существующих блоков языка создавать новые, специфичные для решения конкретной проблемы. Например, работая с такими языками программирования, как Java или C++, часто приходится моделировать исключения (Exceptions) - они являются обыкновенными классами, хотя и рассматриваются особым образом. Обычно требуется, чтобы исключения можно было возбуждать и перехватывать, и ничего больше. Если пометить исключения соответствующим стереотипом, то с ними можно будет обращаться как с обычными строительными блоками языка; на рис. 2.19 это продемонстрировано на примере класса Overflow.


Рис. 2.19 Механизмы расширения

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

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

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


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


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


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




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




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




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




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




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




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



Обзор UML


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



Одиночное наследование


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

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

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

На рис. 5.9 вы видите несколько классов, взятых из приложения по организации работы трейдеров. Здесь показано отношение обобщения, которое от четырех классов - РасчетныйСчет, Акция, Облигация и Собственность - направлено к более общему классу ЦенныеБумаги. Он является родителем, а остальные -его потомками. Каждый специализированный класс - это частный случай класса ЦенныеБумаги. Обратите внимание, что в классе ЦенныеБумаги есть две операции - presentValue (текущаяСтоимость) и history (история). Это значит, что все его потомки наследуют данные операции, а заодно и все остальные атрибуты и операции родителя, которые могут не изображаться на рисунке.


Рис. 5.9 Отношения наследования

Имена ЦенныеБумаги и presentValue на рисунке намеренно выделены курсивом. Дело в том, что, создавая иерархию подобного рода, часто приходится сталкиваться с нелистовыми классами, которые неполны или для которых не может существовать объектов. Такие классы называются абстрактными (см. главу 9), и на языке UML их названия пишутся курсивом, как в приведенном примере. Данное соглашение применимо и к операциям (например, presentValue); оно означает, что у операции есть сигнатура, но в других отношениях она неполна и требует реализации на более низком уровне абстракции (см.
главу 9). В нашем примере все четыре непосредственных потомка класса ЦенныеВумаги конкретны (то есть не абстрактны) и реализуют операцию presentValue.

Иерархия "обобщение/специализация" не обязательно ограничивается двумя уровнями. Как видно из рисунка, вполне допустимо существование более двух уровней наследования. АкцияСМалымКапиталом и АкцияСБолышимКапиталом - потомки класса Акция, который, в свою очередь, является потомком класса Цен-ныеБумаги. Последний является базовым классом, поскольку не имеет родителей. Классы же АкцияСМалымКапиталом и АкцияСБольшимКапиталом - листовые, поскольку не имеют потомков. Наконец, класс Акция имеет как родителей, так и потомков, а следовательно, не является ни листовым, ни базовым.

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

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


Ограничения


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


Рис. 6.7 Ограничение

Примечание: Ограничения могут записываться в свободном формате. Если надо определить семантику более точно, следует воспользоваться входящим в состав UML языком объектных ограничений (Object Constraint Language, OCL). Более подробно он описывается в книге "The Unified Modeling Language Reference Manual". Предопределенные в UML ограничения рассмотрены в "Приложении В".

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


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

В большинстве случаев ограничение размещается рядом с элементом и заключается в фигурные скобки, например {complete}. Можно изображать ограничение и по-другому - помещая в примечание, соединенное с элементом зависимостью.

Ограничение Символ, к которому оно применимо Назначение (о чем говорит данное ограничение)
complete Обобщение (generalization) В модели специфицированы все потомки в данном обобщении (хотя некоторые могут быть скрыты на диаграммах), и дополнительных потомков определять не разрешается
destroyed Экземпляр (instance), связь (link) Экземпляр или связь уничтожаются до завершения выполнения объемлющего взаимодействия
disjoint Обобщение (generalization) Объекты данного родителя могут иметь не более одного заданного потомка в качестве типа
implicit Ассоциация (association) Отношение является не явно выраженным, а концептуальным
incomplete Обобщение (generalization) Специфицированы не все потомки в обобщении (учитывая и скрытых). Разрешается определять дополнительных потомков
new Экземпляр (instance), связь (link) Экземпляр или связь создаются в процессе выполнения объемлющего взаимодействия
or Ассоциация (association) Из множества ассоциаций ровно одна является явно выраженной для каждого ассоциированного объекта
overlapping Обобщение (generalization) Объекты данного родителя могут иметь более одного заданного потомка в качестве типа
transient Экземпляр (instance), связь (link) Экземпляр или связь создаются в процессе выполнения объемлющего взаимодействия, но уничтожаются до его завершения

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

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

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



Операции


Операцией называется реализация услуги, которую можно запросить у любого объекта класса для воздействия на поведение. Иными словами, операция - это абстракция того, что позволено делать с объектом. У всех объектов класса имеется общий набор операций. Класс может содержать любое число операций или не содержать их вовсе. Например, для всех объектов класса Rectangle (Прямоугольник) из библиотеки для работы с окнами, содержащейся в пакете awt языка Java, определены операции перемещения, изменения размера и опроса значений свойств. Часто (хотя не всегда) обращение к операции объекта изменяет его состояние или его данные. Операции класса изображаются в разделе, расположенном ниже раздела с атрибутами. При этом можно ограничиться только именами, как показано на

Более детальная спецификация выполнения операции осуществляется с помощью примечаний (см. главу 6) и диаграмм деятельности (см. главу 19).


Рис. 4.5 Операции

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

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


Рис. 4.6 Операции и их сигнатуры


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

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

Полный синтаксис операции в языке UML таков:

[visibility] name [(parameter-list)] [: return-type] [{property-string}]

Ниже приводятся некоторые допустимые объявления операций:

display - только имя; + display - видимость и имя; set (n: Name, s: String) - имя и параметры; getID() : Integer - имя и возвращаемое значение; restart() {guarded} - имя и свойство.

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

[direction] name : type [= default-value]

Параметр direction может принимать любое из нижеперечисленных значений:

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




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

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


Рис. 11.3 Операции

Примечание: С интерфейсом можно ассоциировать и сигналы (см. главу 20).




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



Операция


Диаграмму деятельности можно присоединить к любому элементу модели для визуализации, специфицирования, конструирования и документирования поведения этого элемента, в частности к классам (см. главы 4 и 9), интерфейсам (см. главу 11), компонентам (см. главу 25), узлам (см. главу 26), прецедентам (см. главу 16) и кооперациям (см. главу 27). Чаще всего диаграммы деятельности присоединяются к операциям.

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

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

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

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

Так, на рис. 19.10 представлена диаграмма деятельности для класса Line (ПрямаяЛиния), которая описывает алгоритм операции пересечение.
Сигнатура алгоритма состоит из одного параметра (1 - входной параметр класса Line) и одного возвращаемого значения (класса Point, Точка). Из атрибутов класса Line интерес представляют два: slope (тангенс угла наклона прямой) и delta (смещение прямой относительно начала координат).


Рис. 19.10 Моделирование операции

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

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


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


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

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


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

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

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



Организация книги и особенности изложения материала


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

Часть 1. Введение в процесс моделирования.
Часть 2. Основы структурного моделирования.
Часть 3. Изучение структурного моделирования.
Часть 4. Основы моделирования поведения.
Часть 5. Более сложные аспекты поведения.
Часть 6. Архитектурное моделирование.
Часть 7. Итоги.

Кроме этого, книга включает в себя три приложения: обзор применяемой в языке UML нотации, список стандартных элементов UML и обзор Рационального Унифицированного Процесса (Rational Unified Process). Приводится глоссарий наиболее распространенных терминов.

Каждая глава посвящена рассмотрению какой-то одной черты UML и, как правило, состоит из следующих четырех разделов:

Введение. Термины и концепции. Типичные приемы моделирования. Советы.

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



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


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

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



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


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

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

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

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

Эти два вида отношений иллюстрируются на рис. 27.4.


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



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


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

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

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


Рис. 16.5 Обобщения, включения и расширения

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

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

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

Основной поток событий. Получить и проверить номер заказа, include (Проверить клиента). Запросить статус каждой части заказа и доложить клиенту.

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

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

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



Основной поток событий include (Проверить Клиента). Собрать все пункты сделанного клиентом заказа. (Установить приоритет). Отправить заказ на обработку.

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

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


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


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

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



Отношения


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

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

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


Рис. 11.4 Визуализация интерфейса

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

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



Переход к UML


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

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

Если у вас нет опыта объектно-ориентированной разработки, воспользуйтесь следующими рекомендациями:

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

Если у вас нет опыта моделирования, поступите так:


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

Если вы уже знакомы с какой-либо другой объектно-ориентированной методикой, поступите следующим образом:

Установите соответствие между элементами используемого вами языка моделирования и элементами UML. В большинстве случаев, особенно если выпользуетесь методиками Booch, OOSE или ОМТ, соответствие будет взаимно-однозначным, и потребуется внести лишь косметические изменения. Рассмотрите какую-нибудь сложную проблему моделирования, которую вам с трудом удалось или вообще не удалось решить с помощью знакомого языка. Выясните, нет ли среди средств UML таких, которые позволят решить ее проще или изящнее.

Если вы опытный пользователь, вам пригодятся следующие советы:

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


Переходы


Когда действие или деятельность в некотором состоянии завершается, поток управления сразу переходит в следующее состояние действия или деятельности. Для описания этого потока используются переходы (Transitions, см. главу 21), показывающие путь из одного состояния действия или деятельности в другое. В UML переход представляется простой линией со стрелкой, как показано на рис. 19.4.


Рис. 19.4 Нетриггерные переходы

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

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


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


Рис. 21.3 Переходы

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

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

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

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

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

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

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

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


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


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

Простым называется такое состояние, которое не имеет внутренней структуры. Состояние, у которого есть подсостояния, то есть вложенные состояния, именуется составным. Оно может содержать как параллельные (независимые), так и последовательные (непересекающиеся) подсостояния. В UML составное состояние изображается так же, как и простое, но имеет дополнительный графический раздел, в котором показан вложенный автомат. Глубина вложенности состояний не ограничена. (Вложенная структура составного состояния подобна композиции - см. главы 5 и 10.)

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

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

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


Рис. 21.5 Последовательные подсостояния

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



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

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

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

Примечание: Вложенный последовательный автомат может иметь не более одного начального и не более одного конечного состояния.

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

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


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

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

В UML для моделирования такой идиомы применяется более простой механизм - исторические состояния (History states). Историческое состояние позволяет составному состоянию, содержащему последовательные подсостояния, запоминать, какое из подсостояний было текущим в момент выхода из составного состояния. На рис. 21.6 недавнее (shallow) историческое состояние представлено в виде небольшого кружочка с символом H.


Рис. 21.6 Историческое состояние

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


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

Примечание: Символом H обозначается недавняя история, в которой запоминается предшествующее состояние только самого внешнего из вложенных автоматов. Можно определить и давнюю (deep) историю, которая изображается кружочком с символом H*. Давняя история способна запомнить последние состояния всех вложенных подавтоматов любого уровня вложенности. При наличии только одного уровня вложенности давняя и недавняя истории семантически эквивалентны. Если же глубина вложенности больше 1, то недавняя история помнит только самое внешнее из вложенных подсостояний, а давняя - все вложенные подсостояния любого уровня.

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

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

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


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

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


Рис. 21.7 Параллельные подсостояния

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

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

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

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


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


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

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

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

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

Примечание: При моделировании полностью распределенной системы саму сеть ча- сто также изображают в виде узла. Например, можно представить Internet, как показано на рис. 30.4, в виде стереотипного узла. Таким же образом позволяется оформить локальную (LAN) или глобальную (WAN) сеть (см. рис. 30.1). В любом случае вы можете воспользоваться атрибутами и операциями узла для описания свойств сети.

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




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


Помеченные значения


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

Метки можно определять для существующих элементов UML; можно также определить метки, применимые к отдельным стереотипам. В последнем случае все элементы, описанные этим стереотипом, будут иметь заданную метку. Помеченное значение - не то же самое, что атрибут класса (см. главы 4 и 9). Скорее, оно может быть представлено как метаданные, поскольку его значение применяется к самому элементу, а не к его экземплярам. Например, как показано на рис. 6.6, в диаграмме развертывания можно указать число процессоров, установленных на узле каждого вида, или потребовать, чтобы каждому компоненту был приписан стереотип библиотеки, если его предполагается развернуть на клиенте или сервере.


Рис. 6.6 Помеченные значения

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

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


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

В большинстве случаев помеченное значение изображается посредством размещения метки и значения под именем элемента, к которому оно присоединено. При этом все сочетание заключается в фигурные скобки, например {location = client }. Если значение метки представляет собой длинный текст, то помеченное значение можно поместить в дополнительный раздел классификатора.

Помеченное значение Символы, к которым оно применимо Назначение
documentation Все элементы Содержит комментарий, описание или пояснение к тому элементу, к которому присоединено
location Большинство элементов Определяет узел или компонент, которому принадлежит элемент
persistence Класс (class), ассоциация (association) атрибут (attribute) Определяет, сохраняется ли состояние , экземпляра после завершения создавшего его процесса. Состояния бывают устойчивыми (сохраняющими значение) или временными (не сохраняющими значение)
semantics Класс (class), операция (operation) Описывает назначение класса или операции



Последовательности


Когда объект посылает сообщение другому объекту (фактически делегируя ему некоторое действие), получатель может, в свою очередь, отправить сообщение третьему объекту, тот - четвертому и т.д. Такой поток сообщений формирует последовательность (Sequence). Она всегда должна иметь начало в некотором процессе или вычислительной нити (см. главу 22); последовательность может продолжаться, пока существует владеющий ею процесс или нить. Постоянно работающая система (см. главу 31), например встроенная в некоторое устройство реального времени, продолжает выполняться, пока не остановлен содержащий ее узел.

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

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


Рис. 15.4 Процедурная последовательность

Менее распространенный, но вполне приемлемый способ показан на рис. 15.5. Здесь линия с незакрашенной стрелкой представляет простой (неструктурированный) поток управления, который моделирует непроцедурную передачу управления от одного шага к другому. В данном случае сообщение assertCall является вторым в последовательности.


Рис. 15.5 Одноуровневая (простая) последовательность

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

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

D5 : ejectHatch(3)

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

Можно показывать не только фактические аргументы, посланные вместе с операцией или сигналом в контексте взаимодействия, но и возвращаемые значения функции. Например, из выражения ниже явствует, что операция find c фактическим параметром "Rachel1е" возвращает значение p. Это вложенная последовательность: операция выполняется в результате второго сообщения, вложенного в третье, которое, в свою очередь, вложено в первое сообщение последовательности. На той же самой диаграмме p можно использовать в качестве фактического параметра других сообщений.

1.3.2 : p := find("Rachel1е")

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


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


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

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

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

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

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


Рис. 20.5 Сигналы и активные классы

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


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


Чаще всего взаимодействия используют для моделирования потока управления, характеризующего поведение системы в целом, включая прецеденты (см. главу 16), образцы, механизмы и каркасы (см. главу 28), поведение одного класса или отдельной операции (см. главы 4 и 9). При этом классы, интерфейсы (см. главу H), компоненты (см. главу 25), узлы (см. главу 26) и отношения между ними моделируют статические аспекты системы, а взаимодействия - ее динамические аспекты (для моделирования последних используются также автоматы, см. главу 21).

Моделируя взаимодействия, вы, по сути дела, описываете последовательности действий, выполняемых объектами из некоторой совокупности. Для выявления и осмысления взаимодействий очень полезно применение CRC-карточек.

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

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

В качестве примера на рис. 15.6 показаны объекты, взаимодействующие в контексте механизма публикации и подписки (экземпляр образца проектирования observer). Вы видите три объекта: p (экземпляр класса StockQuotePublisher), s1 и s2 (экземпляры класса StockQuoteSubscriber). Этот рисунок является примером диаграммы последовательностей (см. главу 18), описывающей временную упорядоченность сообщений.


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

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


Рис. 15.7 Поток управления с точки зрения организации


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

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

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

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

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



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


Рассмотрим объекты, существующие в контексте системы, подсистемы (см. главу 31), операции или класса (см. главы 4 и 9). Рассмотрим также объекты и роли, сопричастные прецеденту (см. главу 16) или кооперации (см. главу 27). Для моде-лирования потока управления, проходящего через эти объекты и роли, применяются диаграммы взаимодействий; при этом, если на первый план нужно вынести передачу сообщений во времени, используют их разновидность - диаграммы последовательностей.

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

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

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

В качестве примера на рис. 18.4 показана диаграмма последовательностей, где описан поток управления, относящийся к инициации простого двустороннего телефонного разговора. На данном уровне абстракции есть четыре объекта: два абонента (Callers) - они названы s и r, безымянный телефонный коммутатор (Switch) и объект с, являющийся материализацией разговора (Conversation) между абонентами. Последовательность начинается с отправки одним абонентом (s) сигнала (см. главу 20) liftReceiver (поднятьТрубку) коммутатору. Коммутатор, в свою очередь, посылает абоненту сообщение setDialTone (задатьТоновыйНаборНо-мера), после чего абонент несколько раз посылает сообщение dialDigit (на-братьЦифру). Обратите внимание, что это сообщение имеет отметку времени (см. главу 23) dialing, которая используется во временном ограничении (время выполнения executionTime - меньше 30 секунд). На диаграмме не показано, что случится при нарушении ограничения (см. главу 6), - для этой цели можно было бы включить отдельную ветвь или нарисовать другую диаграмму. Далее коммутатор посылает сам себе сообщение routeCall (маршрутизировать вызов), а затем создает объект (с) класса Conversation (разговор), которому делегирует остальную часть работы. Хотя это и не показано во взаимодействии, у с есть дополнительная обязанность (см. главу 4), связанная с механизмом начисления платы за разговор (это должно быть выражено на другой диаграмме взаимодействий). Объект Conversation звонит второму абоненту (r), который асинхронно посылает сообщение liftReceiver (поднятие трубки). После этого объект Conversation говорит коммутатору, что надо установить соединение (connect), а коммутатор сообщает обоим абонентам, что они соединены (connect), после чего абоненты наконец могут начать обмен информацией - это и показано в присоединенном примечании (см.главу 6).


Рис. 18.4 Моделирование временной упорядоченности потоков управления

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

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


Поведенческие сущности


Поведенческие сущности - это динамические части моделей UML. К ним относятся взаимодействия (Interactions) и автоматы (State machines).




Поведение


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

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

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


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



Поведение элемента


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

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

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

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

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


Рис. 16.6 Моделирование поведения элемента

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


Правила языка UML


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

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

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

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

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

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



Прецеденты и актеры


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

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


Рис. 16.3 Актеры

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

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



Прецеденты и кооперации


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

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


Рис. 16.4 Прецеденты и кооперации

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

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



Прецеденты и поток событий


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

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

Например, в контексте банкомата можно было бы следующим образом описать прецедент ValidateUser (ПроверитьПользователя).

Основной поток событий. Прецедент начинается, когда система запрашивает у клиента его персональный идентификационный номер (PIN). Клиент (Customer) может ввести его с клавиатуры. Завершается ввод нажатием клавиши Enter. После этого система проверяет введенный PIN и, если он правильный, подтверждает ввод. На этом прецедент заканчивается.

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

Исключительный поток событий. Клиент может в любой момент до нажатия клавиши Enter стереть свой PIN и ввести новый.

Исключительный поток событий. Если клиент ввел неправильный PIN, прецедент запускается сначала. Если это происходит три раза подряд, система отменяет всю транзакцию и не позволяет данному клиенту снова начать работу с банкоматом в течение 60 секунд.

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



Прецеденты и сценарии


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

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

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

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



занимающаяся производством программного обеспечения, может


Компания, занимающаяся производством программного обеспечения, может преуспевать только в том случае, если выпускаемая ею продукция всегда отличается высоким качеством и разработана в соответствии с запросами пользователей. Фирма, которая способна выпускать такую продукцию своевременно и регулярно, при максимально полном и эффективном использовании всех имеющихся человеческих и материальных ресурсов будет стабильно процветать.
Из сказанного следует, что основным продуктом такой компании является именно первоклассное программное обеспечение, удовлетворяющее повседневным нуждам пользователей. Все остальное - прекрасные документы, встречи на высшем уровне, великолепные лозунги и даже Пулитцеровская премия за идеальные строки исходного кода - вторично по сравнению с этой основной задачей.
К сожалению, во многих организациях путают понятия "вторичный" и "несущественный". Нельзя забывать, что для разработки эффективной программы, которая соответствует своему предполагаемому назначению, необходимо постоянно встречаться и работать с пользователями, чтобы выяснить реальные требования к вашей системе. Если вы хотите создать качественное программное обеспечение, вам необходимо разработать прочное архитектурное основание проекта, открытое к возможным усовершенствованиям. Для быстрой и эффективной разработки программного продукта с минимальным браком требуется привлечь рабочую силу, выбрать правильные инструменты и определить верное направление работы. Чтобы справиться с поставленной задачей, принимая во внимание затраты на обеспечение жизненного цикла системы, необходимо, чтобы процесс разработки приложения был тщательно продуман и мог быть адаптирован к изменяющимся потребностям вашего бизнеса и технологии.
Центральным элементом деятельности, ведущей к созданию первоклассного программного обеспечения, является моделирование. Модели позволяют нам наглядно продемонстрировать желаемую структуру и поведение системы. Они также необходимы для визуализации и управления ее архитектурой. Модели помогают добиться лучшего понимания создаваемой нами системы, что зачастую приводит к ее упрощению и возможности повторного использования. Наконец, модели нужны для минимизации риска.


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


Авторы языка Си (С) Брайан Керниган (Brian Kernighan) и Деннис Ричи (Dennis Ritchie) сказали как-то, что единственный способ выучить новый язык программирования - это писать на нем программы. Данное замечание верно и в отношении UML. Чтобы выучить этот язык, надо писать на нем модели.
Первая программа, которую обычно пишут новички, осваивая незнакомый язык, очень проста - она предполагает вывод на экран текстовой строки "Здравствуй, мир!". Это резонный подход, поскольку воплощение даже такого элементарного замысла вызывает вполне объяснимое чувство удовлетворения. Кроме того, как бы тривиальна ни была данная программа, она содержит всю инфраструктуру, необходимую для того, чтобы приложение могло работать.
Освоение UML мы начнем традиционным способом. Моделирование вывода фразы "Здравствуй, мир!" - пожалуй, простейшее применение языка, но эта простота обманчива, поскольку за ней скрыты некоторые любопытные механизмы, заставляющие приложение функционировать. С помощью UML эти механизмы легко моделируются. Поняв их, вы глубже проникнете в структуру этой незамысловатой программы.

Представление


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

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

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



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


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



А. Нотация UML


Сущности

Структурные сущности

Поведенческие сущности

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

Аннотационные сущности

Отношения

Зависимость

Ассоциация

Обобщение

Расширение

Диаграммы



С. Рациональный Унифицированный Процесс


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

Фазы и итерации

Фазы

Итерации

Циклы разработки

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

Артефакты

Модели

Переход к UML



Содержащее комментарий примечание не оказывает


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

Рис. 6.3 Примечания
Примечание: В UML определен один стандартный стереотип, прилагаемый к примечаниям, - требования (Requirements). Так именуется распространенная категория примечаний, используемая для формулирования обязанностей или обязательств

Примитивные типы


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

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

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

Рис. 4.12 показывает, что такие сущности можно моделировать в UML как типы (см. главу 11) или перечисления, которые изображаются точно так же, как классы, но с явно указанным стереотипом. Сущности, подобные целым числам (представленные классом Int), моделируются как типы, а с помощью ограничений вы можете явно указать диапазон принимаемых значений. Перечислимые типы (скажем, Boolean и Status) допустимо моделировать как перечисления, причем их конкретные значения становятся атрибутами.


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

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



Принципы моделирования


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

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

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

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

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

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

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

Третий принцип: лучшие модели - те, что ближе к реальности.



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

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

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

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

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

Такой подход верен и в отношении объектно-ориентированных программных систем.


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

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


Принятые в книге обозначения


Для облегчения работы с текстом в книге приняты следующие соглашения:

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

Примечание: Текст примечания содержит не относящиеся непосредственно к теме изложения комментарии и общие рекомендации.

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

Издательство "ДМК" заинтересовано в получении ваших отзывов об этой книге. Напишите нам, какие еще книги по программированию вы хотели бы прочитать. Свои отклики и пожелания присылайте на наш Web-сайт http://www.dmk.ru.

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

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