double arrow

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

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

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

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

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

Одно из преимуществ диаграммы последовательности заключается в том, что мне почти не придется объяснять ее нотацию. Можно видеть, что экземпляр заказа посылает строке заказа сообщения getQuantity и getProduct. Можно также видеть, как заказ применяет метод к само­му себе и как этот метод посылает сообщение getDiscountlnfo экземпля­ру клиента.

Однако диаграмма не все показывает так хорошо. Последовательность сообщений getQuantity, getProduct, getPricingDetails и calculateBasePrice должна быть реализована для каждой строки заказа, тогда как метод calculateDiscounts вызывается лишь однажды. Такое заключение нель-


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

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

На приведенной диаграмме я именовал участников, используя стиль anOrder. В большинстве случаев это вполне приемлемо. Вот более пол­ный синтаксис: имя: Класс, где и имя, и класс не обязательны, но если класс используется, то двоеточие должно присутствовать. (Этот стиль выдержан на рис. 4.4.)

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


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

Именование бывает часто полезным для установления связей между участниками на диаграмме. Как видно на диаграмме, вызов метода getProduct возвращает aProduct, имеющего то же самое имя и, следова­тельно, означающего того же самого участника, aProduct, которому по­сылается вызов getPricingDetails. Обратите внимание, что обратной стрелкой я обозначил только этот вызов с целью показать соответст­вие. Многие разработчики используют возвраты для всех вызовов, но я предпочитаю применять их, только когда это дает дополнительную информацию; в противном случае они просто вносят неразбериху. Не исключено, что даже в данном случае можно было опустить возврат, не запутав читателя.

У первого сообщения нет участника, пославшего его, поскольку оно приходит из неизвестного источника. Оно называется найденным со­общением (found message).

Другой подход можно увидеть на рис. 4.2. Основная задача остается той же самой, но способ взаимодействия участников для ее решения совершенно другой. Заказ спрашивает каждую строку заказа о его соб­ственной цене (Price). Сама строка заказа передает вычисление даль­ше - объекту продукта (Product); обратите внимание, как мы показы­ваем передачу параметра. Подобным же образом для вычисления скидки объект заказа вызывает метод для клиента (Customer). По­скольку для выполнения этой задачи клиенту требуется информация от объекта заказа, то он делает повторный вызов в отношении заказа для получения этих данных.


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

Во-вторых, посмотрите, как четко видна разница в стиле между двумя взаимодействиями. На рис. 4.1 представлено централизованное управ­ление (centralized control), когда один из участников в значительной степени выполняет всю обработку, а другие предоставляют данные. На рис. 4.2 изображено распределенное управление (distributed control), при котором обработка распределяется между многими участниками, каждый их которых выполняет небольшую часть алгоритма.

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

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

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

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



Понравилась статья? Добавь ее в закладку (CTRL+D) и не забудь поделиться с друзьями:  



Сейчас читают про: