Диаграммы прецедентов: крупным планом
Как видите, для каждой ассоциации на диаграмме проставлена кратность и ее смысл вполне понятен, но все же о кратности следует поговорить отдельно. Один прецедент определяет несколько сценариев, каждый из которых представляет один из возможных вариантов определяемого прецедентом потока событий. Сценарии так же соотносятся с прецедентами, как экземпляры класса, т.е. сценарий — это экземпляр прецедента, как объект — экземпляр класса. Система может содержать, например, несколько десятков прецедентов, каждый из которых, в свою очередь , может разворачиваться в десятки сценариев. Как правило, прецедент описывает не одну последовательность действий, а множество, и выразить все детали рассматриваемого прецедента с помощью одной последовательности действий обычно не получается. Практически для любого прецедента можно выделить основной сценарий , описывающий «нормальную» последовательность действия, и вспомогательные, описывающие альтернативные последовательности, которые инициируются в случае возникновения определенных условий.
Другой вопрос: требуется ли такое уточнение модели прецедентов, оправдано ли оно для данного уровня приближения, или «подразумевающиеся» альтернативные сценарии можно опустить? Например, в предыдущем примере с покупкой билета в сервисной кассе мы не изобразили сценарии (и, соответственно, прецеденты), соответствующие вариантам, когда билетов на выбранный пассажиром рейс уже не осталось, пассажир изменил свое решение и хочет взять билет на другой рейс, когда оплата идет наличными или по кредитной карте и т. д.
«Хватит ходить вокруг да около!» — воскликнет нетерпеливый читатель. Уже заканчиваем. Мы просто хотели мягко подвести читателя к вопросу об отношениях между прецедентами. А отношения эти весьма многообразны. Начнем со старого знакомого — отношения обобщения (наследования, генерализации). О генерализации мы уже говорили не раз, когда рассматривали диаграммы классов . Но все же напомним суть этого понятия. Как говорят классики, обобщение — это отношение специализации (обобщения), в котором объекты специализированного элемента (потомка) могут быть подставлены вместо объектов обобщенного элемента (родителя, или предка).
Точно так же, как мы обычно поступаем с классами, после того как мы выделили и описали каждый прецедент , мы должны просмотреть их все на предмет наличия одинаковых действий — поискать, а не выполняются ли (используются) некоторые действия совместно несколькими вариантами использования. Этот совместно используемый фрагмент лучше описать в отдельном прецеденте. Таким образом мы уменьшим избыточность модели за счет применения обобщения прецедентов (иногда, правда, говорят не об обобщении, а об использовании прецедентов; почему — сейчас поймете). Как это и «положено» при наследовании, экземпляры обобщенных прецедентов (потомков) сохраняют поведение, присущее обобщающему прецеденту (предку). Другими словами, наличие (использование) в варианте использования X обобщенного варианта использования Y говорит нам о том, что экземпляр прецедента X включает в себя поведение прецедента Y . Обобщения применяются, чтобы упростить понимание модели вариантов использования за счет многократного задействования «заготовок» прецедентов для создания прецедентов, необходимых заказчику (помните, как мы рассматривали вопрос о том, всегда ли необходимо создавать новый класс , или лучше воспользоваться готовым решением, чувствуете аналогию?). Такие «полные» прецеденты называются конкретными прецедентами. «Заготовки» прецедентов, созданные лишь для многократного использования в других прецедентах, называют абстрактными прецедентами. Абстрактный прецедент (как и абстрактный класс ) не существует сам по себе, но экземпляр конкретного прецедента демонстрирует поведение, описываемое абстрактными прецедентами, которые он (повторно) использует. Прецедент , который экторы наблюдают при взаимодействии с системой («полный» прецедент , как мы называли его ранее), часто называют еще » реальным » прецедентом.
Как мы уже говорили выше, обобщение ( наследование ) чаще всего используют между классами и интерфейсами. Однако другие элементы модели также могут находиться между собой в отношении наследования — например, пакеты (о которых мы тут не говорим), экторы, прецеденты.
Изображается обобщение , как, конечно, помнит внимательный читатель, линией с «незакрашенной» треугольной стрелкой на конце. Обобщение — это отношение между предком и потомком, и стрелка всегда указывает на предка. Если вспомнить, что потомки наследуют (используют) свойства предка, то вполне логично вспоминается наше утверждение о том, что стрелки в UML всегда направлены в сторону того, от кого что-то требуют, чьими сервисами пользуются (рис. 6.9):
Как мы уже говорили ранее и видели в нашем первом примере диаграммы прецедентов , обобщение может использоваться для создания различных разновидностей экторов. Экторы-потомки наследуют от предка базовые характеристики и дополняют их своей спецификой. Точно так же прецедент — потомок наследует поведение и семантику прецедента-родителя и дополняет его поведение.
Следующий вид отношений между прецедентами — включение. Отношение включения означает, что в некоторой точке базового прецедента содержится поведение другого прецедента. Включаемый прецедент не существует сам по себе, а является всего лишь частью объемлющего прецедента. Таким образом, базовый прецедент как бы заимствует поведение включаемых, раскладываясь на более простые прецеденты. Например, когда мы покупаем в магазине некоторую вещь, в момент считывания кассиром штрих-кода обновляется состояние базы данных товаров, имеющихся в наличии, — количество наличных единиц купленного товара уменьшается. То же самое действие выполняется и в том случае, если купленный товар оказался бракованным, непригодным к использованию или попросту нам не понравился: состояние упомянутой базы данных вновь обновляется — но теперь уже в сторону увеличения количества наличных единиц определенного товара. Т. е. оба этих действия — и покупка, и возврат — содержат (включают в себя) такое действие, как обновление содержимого БД .
А как же изображается включение? Да очень просто — как зависимость (пунктирная линия со стрелкой, помните?) со стереотипом > . При этом стрелка направлена, естественно, в сторону включаемого прецедента. Этот факт легко объяснить, если вспомнить утверждение, которое мы уже несколько раз использовали в этом курсе: стрелка всегда направлена в сторону того элемента, от которого что-то требуется, чьими сервисами пользуются. А если считать, что объемлющий прецедент включает в себя, заимствует (использует) поведение включаемых прецедентов, становится ясно, что стрелка может быть направлена только таким образом. А вот и диаграмма , иллюстрирующая вышесказанное, которую мы позаимствовали из Zicom Mentor (рис. 6.10):
Как хорошо видно из этого примера, использование включения позволяет избежать многократного описания одного и того же набора действий — общее поведение можно просто описать в виде прецедента, включаемого в базовые.
На очереди — отношение расширения. Чтобы уяснить себе смысл расширения, представим себе, что мы говорим об оплате некоторого купленного нами товара. Мы можем оплатить товар наличными, если сумма не превышает $ 100. Или оплатить кредитной картой, если сумма находится в пределах от $ 100 до $ 1000. Если же сумма превышает $ 1000, нам придется брать кредит . Таким образом мы расширили понимание операции оплаты купленного товара и на случаи, когда используются другие средства оплаты, нежели наличные. Но сами эти случаи возникают только при строго определенных условиях: когда цена товара попадает в определенные рамки.
Расширение дополняет прецедент другими прецедентами, «срабатывающими» при некоторых условиях, — просто добавляет в исходный прецедент последовательность действий, содержащуюся в другом прецеденте. Отношение расширения прецедента А к прецеденту В означает, что экземпляр прецедента В может включать в себя (при определенных условиях, которые могут быть описаны в расширении; как именно описаны, мы скажем чуть позже) поведение, описанное в прецеденте А. Пример показан на следующей диаграмме (рис. 6.11):
Однако в приведенном примере не видно, при каких именно условиях человек использует каждый конкретный способ оплаты. В то же время, при моделировании с использованием расширения можно указать как условия осуществления расширенного поведения, так и место — точку расширения прецедента, в которой подключаются действия из расширяющих прецедентов. Вспомните оператор безусловного перехода , который вы, надеемся, использовали в своих программах не слишком часто. Как только интерпретатор доходит до этого оператора, он передает управление на строку, которая помечена меткой, указанной в этом операторе. Правда, в случае расширения речь идет скорее об операторе условного перехода — когда исходный прецедент (а именно, последовательность действий, содержащаяся в нем) приходит в точку расширения, происходит оценка условий расширения. Если условия выполняются, прецедент включает в себя последовательность действий из расширяющего прецедента.
Точка расширения описывается в дополнительном разделе прецедента, отделенном от его названия горизонтальной линией — точно так же, как в отдельных разделах перечисляются атрибуты класса и его операции . Ниже показан пример описания точки расширения, позаимствованный нами из Zicom Mentor (рис. 6.12).
В этом примере регистрация пассажиров авиарейса включает в себя контроль службы безопасности, а при условии (указанном в примечании после служебного слова » Condition :»), что человек часто летает и салон переполнен (обратите внимание на оператор AND , говорящий об одновременности выполнения условий), класс билета может быть повышен, например, с «эконом» до «бизнес-класса». Причем такой апгрейд может произойти только после того, как билет предъявлен на стойку регистрации — это и есть точка расширения. Она описана (ее имя указано) в дополнительном разделе прецедента после служебной фразы » Extension points:». Предваряя вопрос читателя, скажем, что прецедент может иметь сколь угодно много точек расширения. А сопоставить конкретный расширяющий прецедент с определенной точкой расширения можно, прочитав условия расширения, указанные в комментариях, — само условие записывается после служебного слова » Condition :» в фигурных скобках, за которыми идет служебная фраза » Extension point :», и после нее указывается имя точки расширения. Посмотрите еще раз на наш пример с регистрацией пассажиров в аэропорту и убедитесь сами, что все это очень просто!
Некоторое недоумение может вызвать то, что стрелка направлена всегда в сторону расширяемого прецедента. Но и это легко объяснить с точки зрения нашего тезиса, что «стрелка всегда указывает на того, от которого что-то требуют»: ведь для того, чтобы прецедент был расширен, нужно, чтобы он попал в точку расширения и проверилась истинность условий — только тогда действия, содержащиеся в расширяющем прецеденте, смогут быть добавлены в последовательность действий исходного прецедента. Так что все правильно — от расширяемого прецедента требуется точка расширения и проверка условий, потому и стрелка направлена к нему.
Подытоживая все вышесказанное, можно сказать, что расширение позволяет моделировать необязательное поведение системы (был бы класс билета повышен, если бы пассажир не налетал нужного количества миль, а салон был бы почти пуст?). Сам факт расширения зависит от выполнения условий — расширения ведь может и не произойти! Это просто отдельные последовательности действий, выполняемые лишь при определенных обстоятельствах и включаемые в определенных точках сценария (обычно в результате явного взаимодействия с эктором).
Организация прецедентов с помощью выделения общего поведения (включение) и различных вариантов поведения (расширение) — важная составляющая часть процесса разработки простого, сбалансированного и понятного набора прецедентов. Можно сказать даже, что использование включения и расширения — признак хорошего стиля в моделировании прецедентов.
На этом разговор о нотации диаграмм прецедентов можно было бы и завершить. Хотелось бы только сказать еще пару слов о соотношении между понятиями прецедента и кооперации. О кооперации мы уже говорили ранее (помните диаграммы взаимодействия ?) как о множестве ролей, работающих вместе, чтобы обеспечить некоторое поведение системы. Мы также упоминали о том, что прецеденты отвечают на вопрос «что делает система?», но не говорят, как именно она это делает. На этапе анализа понимать, как именно система реализует свое поведение, действительно не нужно. Но при переходе к реализации неплохо бы знать, какие именно классы (или другие элементы модели), совместно работая, обеспечивают нужное поведение. То есть мы логично перешли от разговора о прецедентах к разговору о кооперации! Недаром обозначения кооперации и прецедента очень похожи (читатель, конечно, помнит, что кооперация обозначается пунктирным эллипсом) (рис. 6.13).
Так в каком же отношении находятся прецедент и кооперация ? Из предыдущего абзаца логично следует, что это отношение реализации. Каждый прецедент реализуется одной или несколькими кооперациями. Это, конечно, не означает, что классы жестко распределены по кооперациям: классы, принимающие участие в кооперации, реализующей определенный прецедент , будут участвовать и в других кооперациях.
Источник
Диаграммы прецедентов: крупным планом
Моделирование при помощи диаграмм прецедентов
Модель прецедентов, по сути, является концептуальной моделью системы. В ней, как мы уже не раз отмечали, в общих чертах описывается только поведение (функциональность) системы, а о деталях реализации речь не идет — на данном этапе реализация не важна, гораздо важнее собрать требования к системе и оформить их в наглядном виде, понятном и разработчикам, и заказчику.
Итак, подводя итоги, мы можем сформулировать три причины использования прецедентов. Или, вернее, три способа использования прецедентов (не случайно в русском переводе частенько можно встретить словосочетание » вариант использования «!) в ходе работы над системой:
- Прецеденты дают возможность аналитикам, пользователям и разработчикам говорить на одном языке: используя прецеденты, аналитики (эксперты в предметной области) могут на основе пожеланий заказчика описать поведение системы с точки зрения пользователя с такой степенью детализации, что разработчики смогут без труда сконструировать «внутренности» системы. В то же время, нотация диаграмм прецедентов настолько проста, что даже неподготовленный пользователь (заказчик) способен понять их смысл и помочь в их уточнении — ведь картинки (а тем более комиксы, каковыми, по сути, являются диаграммы UML) воспринимаются намного легче, чем текст!
- Прецеденты позволяют разработчикам понять назначение элемента: система, подсистема или даже класс могут быть сложными образованиями, состоящими из большого числа составных частей и имеющими большое число атрибутов и операций. Моделирование прецедентов позволяет лучше представить себе поведение системы, понять, какие элементы модели играют какие роли в реализации этого поведения, в какие кооперации входят, и какой именно прецедент (функционал системы) реализуют.
- Прецеденты являются основой для тестирования элемента в течение всей разработки: модель прецедентов описывает желаемое поведение системы (ее функционал) с точки зрения пользователя. Так что, постоянно сопоставляя предоставляемый элементом (фактический) функционал с имеющимися прецедентами, можно надежно контролировать корректность реализации элемента. Вот вам и надежный источник регрессионных тестов. Кроме этого, появление нового прецедента зачастую заставляет пересмотреть реализацию элемента, дабы убедиться, что она обладает достаточной гибкостью, изменяемостью и масштабируемостью.
Прецеденты полезны и для прямого, и для обратного проектирования. При прямом проектировании мы, по сути, осуществляем «перевод» с UML на некий язык программирования . И тестировать созданное приложение следует, основываясь именно на потоках событий, описываемых прецедентами. Обратное проектирование предполагает перевод с языка программирования на язык UML -диаграмм. Такими вещами приходится заниматься в силу ряда причин:
- С целью поиска ошибок и чтобы убедиться в адекватности дизайна:
отличная идея после первого перевода с UML на язык программирования сделать обратный перевод и сравнить исходные и восстановленные UML-модели (желательно, чтобы эти переводы выполнялись разными командами). Это позволит убедиться в том, что дизайн системы соответствует модели, никакая информация в ходе перевода не была утеряна, да и попросту выловить некоторые «баги». Такой подход называется обратной семантической трассировкой (или RST — Reverse Semantic Traceability ) и разрабатывается компанией INTSPEI (http://www.intspei.com) как одна из базовых техник методологии INTSPEI P-Modeling Framework, краткие сведения о которой вы можете найти в приложении к этому курсу.
И наконец, следует отметить, что, конечно, только диаграмм прецедентов, как и сценариев, ими определяемых, недостаточно, чтобы создать модель поведения системы. Как мы уже не раз упоминали, прецеденты говорят, что делает система, но не говорят, как. Об этом говорят сценарии, но в текстовой форме, что делает их довольно сложными для восприятия. На помощь приходят диаграммы взаимодействий, которые визуализируют сценарии. Таким образом, мы теперь можем дополнить нашу старую «псевдодиаграмму» и на этом успокоиться (рис. 6.14):
В заключение приведем пару примеров законченных диаграмм прецедентов. Первый пример (смысл которого понятен и без дополнительных пояснений) демонстрирует включение, расширение и наследование прецедентов. Обратите внимание на стрелки, которые направлены к экторам, изображающим шлюзы. Все правильно — ведь система пользуется их услугами при отправке сообщений, в то время как маркетолог, наоборот, пользуется услугами системы, и потому стрелки направлены от него (рис. 6.15).
Следующие три примера уже по традиции мы позаимствовали с сайта шуток на UML (http://www.umljokes.com), продолжая доказывать, что на UML можно шутить — это полноценный язык общения, который можно применять так же, как и любой другой. Первый из примеров — это часть всем известной сказки о «Курочке Рябе», которую автор очень красочно оформил (рис. 6.16).
Вторая диаграмма , тоже неплохо оформленная, говорит нам о том, что утки очень не любят платить за пиво, предпочитая пить в долг (рис. 6.17).
Кстати, обратите внимание на рамки диаграммы, показанные на этом примере, — прямоугольник , отделяющий область содержимого диаграммы и имеющий в верхней части специальный раздел для ее имени.
И наконец, третья картинка, которая не является хорошим примером диаграммы прецедентов , но просто забавна. Это рассказ о способах поведения, позволяющих гарантированно (!) провалить любой экзамен (рис. 6.18):
Источник