UML2 и унифицированный процесс. Джим арлоуайла нейштадтпрактический объектно ориентированныйанализ и проектированиеu
Скачать 6.08 Mb.
|
• порядковые номера в качестве префиксов на коммуникационных диаграммах (раздел 20.5.3); • различные представления временных диаграмм (раздел 20.7); • ортогональные составные состояния на диаграммах состояний (раз дел 22.2.2). В следующем разделе рассматриваются активные классы, а затем об суждаются параллелизм на диаграммах последовательностей и ком муникационных диаграммах. :Registrar :RegistrationUI uml:Course addCourse( "UML" ) uml= Course("UML") sd AddCourse – проектирование :RegistrationManager :DBManager save(uml) Это иллюстрирует центральный механизм сохранения объектов, который должен использоваться по всей системе addCourse( "UML" ) Рис. 20.5. Диаграмма последовательностей на ранних этапах проектирования 20.5. Моделирование параллелизма 453 20.5.1. Активные классы Параллелизм – каждый активный объект имеет собственный поток вы полнения. Основной принцип моделирования параллелизма – каждый поток управления или параллельный процесс моделируется как активный объект . Под последним понимают объект, инкапсулирующий собст венный поток управления. Активные объекты являются экземпляра ми активных классов. Активные объекты и активные классы изобра жаются на диаграммах как обычные классы и объекты, но с двойной рамкой справа и слева, как показано на рис. 20.8. Параллелизм обычно имеет большое значение для встроенных систем, таких как программное обеспечение, управляющее минифотолаборато рией или банкоматом. Для изучения параллелизма рассмотрим очень простую встроенную систему – систему безопасности. Она отслежива ет ряд датчиков для обнаружения пожара или проникновения в поме щение взломщиков. При срабатывании датчика система включает сигнализацию. Модель прецедентов для системы безопасности показа на на рис. 20.6. Описания прецедентов системы приведены на рис. 20.7. Прецедент Ac tivateFireOnly (активировать только пожарные датчики) не рассматрива ется, поскольку основное внимание в этом разделе направлено на ас пекты параллелизма системы. Кроме того, эти прецеденты довольно абстрактны и отражают лишь суть того, что должна делать система сигнализации. Более подробно ее поведение будет рассмотрено позже. Теперь надо найти классы. Для встроенных систем превосходным ис точником классов могут быть аппаратные средства, на которых систе ма выполняется. На практике наилучшей программной архитектурой обычно является та, которая максимально близка к архитектуре фи DeactivateSystem Система безопасности SecurityGuard Fire Intruder TriggerSensor ActivateFireOnly ActivateAll Рис. 20.6. Модель прецедентов для системы безопасности 454 Глава 20. Реализация прецедента на этапе проектирования зического оборудования. В рассматриваемом случае сигнализация со стоит из четырех компонентов: блока управления, сирены, набора по жарных датчиков и набора датчиков безопасности. Если заглянуть в лок управления, там можно найти плату контроллера для каждого из типов датчиков. Исходя из прецедентов и информации о физическом оборудовании можно получить диаграмму классов для этой системы, представлен ную на рис. 20.8. Прецедент: ActivateAll (АктивироватьВсе) Главные актеры: SecurityGuard Предусловия: 1. У SecurityGuard есть ключ активации. Постусловия: 1. Система безопасности активирована. 2. Система безопасности отслеживает датчики. Основной поток: 1. SecurityGuard применяет ключ активации для включения системы. 2. Система начинает отслеживать датчики безопасности и пожарные датчики. 3. Система воспроизводит звук сирены, демонстрируя готовность. Альтернативные потоки: Нет. ID: 2 Краткое описание: Активирует систему. Второстепенные актеры: Нет. Прецедент: DeactivateSystem Главные актеры: SecurityGuard Предусловия: 1. У SecurityGuard есть ключ активации. Постусловия: 1. Система безопасности деактивирована. 2. Система безопасности не отслеживает датчики. Основной поток: 1. SecurityGuard применяет ключ активации для выключения системы. 2. Система прекращает отслеживание датчиков безопасности и пожарных датчиков. Альтернативные потоки: Нет. ID: 1 Второстепенные актеры: Нет. Краткое описание: Деактивирует систему. Прецедент: T riggerSensor Главные актеры: Fire Intruder Предусловия: 1. Система безопасности активирована. Постусловия: 1. Звучит сигнал Сирены. Основной поток: 1. Если актер Fire инициирует пожарный датчик (FireSensor). 1.1. Сирена воспроизводит сигнал пожарной тревоги. 2. Если актер Intruder инициирует датчик безопасности (SecuritySensor). 2.1. Сирена воспроизводит сигнал тревоги. Альтернативные потоки: Нет. ID: 3 Краткое описание: Срабатывает датчик. Второстепенные актеры: Нет. Рис. 20.7. Описание прецедентов системы безопасности ControlBox SecuritySensorMonitor FireSensorMonitor SecuritySensor FireSensor 1 Siren 1 1 1 1 1 1 0..* 0..* активный класс Рис. 20.8. Диаграмма классов системы безопасности 20.5. Моделирование параллелизма 455 Экземплярами активных классов являются активные объекты. Система безопасности должна непрерывно отслеживать пожарные дат чики и датчики безопасности, поэтому необходимо использовать мно гопоточность (multithreading). Классы ControlBox (блок управления), SecuritySensorMonitor (монитор датчиков безопасности) и FireSensorMonitor (монитор пожарных датчиков) показаны с двойными рамками справа и слева. Это означает, что они являются активными классами. 20.5.2. Параллелизм на диаграммах последовательностей Теперь мы имеем достаточное количество информации для создания диаграммы последовательностей. Диаграмма последовательностей для прецедента ActivateAll показана на рис. 20.9. Она демонстрирует исполь зование операторов par, loop и critical. :Security Guard :ControlBox :FireSensor Monitor :SecuritySensor Monitor activate() monitor() :FireSensor :Security Sensor :Siren par monitor() fire = isT riggered() loop 1, * [!fire] fire() intruder() sd ActivateAll soundActivatedAlarm() soundFireAlarm() intruder = isT riggered() soundIntruderAlarm() loop 1, * [(!intruder) & (!fire)] critical opt [!fire] Рис. 20.9. Диаграмма последовательностей для прецедента ActivateAll 456 Глава 20. Реализация прецедента на этапе проектирования Ниже приведен поэтапный анализ диаграммы, представленной на рис. 20.9 . 1. Объект :SecurityGuard отправляет сообщение activate() объекту :Control Box. 2. :ControlBox отправляет сообщение soundActivatedAlarm() объекту :Siren. 3. :ControlBox порождает два потока управления, представленные опе рандами оператора par. Перемещаясь вниз по диаграмме, сначала вызывается операнд 1 оператора par, а затем операнд 2 оператора par. 4. Операнд 1 оператора par: 4.1. :ControlBox отправляет сообщение monitor() объекту :FireSensor Monitor. 4.2. :FireSensorMonitor входит в цикл для опроса :FireSensor. При пер вом выполнении цикла задается начальное значение перемен ной fire, затем цикл продолжается до тех пор, пока fire имеет значение false. 4.3. Когда fire принимает значение true: 4.3.1. :FireSensorMonitor входит в раздел critical, где: 4.3.1.1. Он посылает сообщение fire() в :ControlBox. 4.3.1.2. :ControlBox посылает сообщение soundFireAlarm() объекту :Siren. 5. Операнд 1 оператора par завершается. 6. Операнд 2 оператора par: 6.1. :ControlBox посылает сообщение monitor() объекту :SecuritySensor Monitor. 6.2. :SecuritySensorMonitor входит в цикл для опроса :SecuritySensor. При первом выполнении цикла задается начальное значение переменной intruder, затем цикл продолжается до тех пор, пока intruder И (AND) fire имеют значение false. 6.3. Когда intruder принимает значение true: 6.3.1. Если fire имеет значение false: 6.3.1.1. :SecuritySensorMonitor посылает сообщение intru der() объекту :ControlBox; 6.3.1.2. :ControlBox посылает сообщение soundIntruder Alarm() объекту :Siren. 7. Операнд 2 оператора par завершается. 8. Взаимодействие завершается. В этом взаимодействии следует отметить несколько интересных мо ментов: • Оба операнда par выполняются параллельно. 20.5. Моделирование параллелизма 457 • Раздел critical представляет атомарное поведение, которое не может быть прервано. Это важное уточнение, поскольку вызов пожарного датчика является критически важным для безопасности и не может быть прерван. • Оба цикла ( loop) имеют семантику Repeat...Until (повторять ... пока) – они выполняются один раз, чтобы задать значение переменной, ис пользуемой в их условиях, и затем повторяются до тех пор, пока их условия остаются истинными. • Пожарная тревога всегда должна иметь больший приоритет по от ношению к тревоге вторжения. Поэтому loop в операнде 2 оператора par прерывается событием fire() или intruder(). Зачем продолжать от слеживание вторжений во время пожара! Более того, сигнал трево ги вторжения воспроизводится только в случае отсутствия пожар ной тревоги. Еще хочется отметить, что на этой диаграмме показан только один Fire Sensor (пожарный датчик) и один SecuritySensor (датчик безопасности). Конечно, этого достаточно для иллюстрации поведения, но, возможно, вам захочется показать итерацию системы по нескольким датчикам SecuritySensor и нескольким FireSensor. Для этого придется изменить диа грамму так, как показано на рис. 20.10, и ввести еще два внутренних цикла для прохода по коллекции датчиков. Оба операнда на рис. 20.10 с точки зрения цикла ведут себя одинако во, поэтому в качестве примера был выбран верхний из них, который отслеживает FireSensor. Как видите, внешний цикл остался неизменным. А новый внутренний цикл по очереди перебирает все датчики FireSensor. Это можно показать с помощью выражения цикла: [for each f in FireSensor] Тогда селектор [f] может использоваться для обозначения на диаграмме последовательностей FireSensor, выбранного циклом в качестве линии жизни, и ему может быть отправлено сообщение isTriggered(). В этом цик ле есть комбинированный фрагмент break, который прерывает внут ренний цикл и выполняет свой операнд, когда fire принимает значение true. При этом, как и ранее, мы попадаем в критическую секцию. Важно сохранять максимальную простоту диаграмм последовательно стей. Основное внимание в реализации прецедента должно быть на правлено на иллюстрацию возможных взаимодействий классов при реализации описанного прецедентом поведения. Для данного примера прецедента ActivateAll диаграммы, показанной на рис. 20.9, вероятно, достаточно для представления основного поведения системы, особенно если снабдить ее несколькими комментариями. А рис. 20.10 все таки слишком подробный. 458 Глава 20. Реализация прецедента на этапе проектирования 20.5.3. Параллелизм на коммуникационных диаграммах Для обозначения разных потоков управления после порядкового номе ра указывается метка потока. Параллелизм на коммуникационных диаграммах изображают с помо щью меток потоков управления, которые указываются после порядко вого номера операции, как показано на рис. 20.11. Данная коммуни кационная диаграмма представляет то же взаимодействие, что и на рис. 20.9. Здесь есть два параллельных потока управления, A и B. :Security Guard :ControlBox :FireSensor Monitor :SecuritySensor Monitor activate() monitor() [f]: FireSensor [s]:Security Sensor :Siren par monitor() intruder = isT riggered() fire = isT riggered() fire() sd ActivateAll loop 1,* [!fire] break [fire] critical loop 1, * [(!intruder) & (!fire)] loop [for each s in SecuritySensor] break [intruder & (!fire)] intruder() soundIntruderAlarm() soundFireAlarm() soundActivatedAlarm() loop [for each f inFireSensor] Рис. 20.10. Диаграмма последовательностей для коллекции датчиков 20.6. Взаимодействия подсистем 459 В этом примере предполагается, что есть всего один экземпляр FireSen sor и один экземпляр SecuritySensor и что итерация является операцией последовательного опроса, постоянно вызывающей операцию isTrig gered() до тех пор, пока датчик возвращает значение true. А если бы датчиков было много, как предполагалось для диаграммы последовательностей на рис. 20.10? Тогда пришлось бы постоянно про ходить набор экземпляров датчиков, опрашивая их по очереди. Для этого необходим вложенный цикл. На коммуникационных диаграм мах можно показать вложенные циклы, но нет способа сделать это про сто и ясно! В сложных случаях рекомендуется использовать диаграм мы последовательностей. Их синтаксис более четкий и гибкий. 20.6. Взаимодействия подсистем На диаграммах взаимодействий подсистем можно показать взаимодей ствия между частями системы. После создания физической архитектуры подсистем и интерфейсов мо жет оказаться полезным смоделировать взаимодействия между под системами. Это обеспечивает очень удобное высокоуровневое представ ление того, как архитектура реализует прецеденты, без перехода к бо лее низкоуровневым деталям взаимодействий отдельных объектов. Каждая подсистема рассматривается как черный ящик, который про сто предоставляет и требует сервисы, описанные как предоставляемые и требуемые интерфейсы. О взаимодействиях объектов внутри подсис темы вообще не надо беспокоиться. 1.1.1 A * [!fire] : fire = isT riggered() :FireSensorMonitor :SecuritySensorMonitor :ControlBox 1: activate () :SecurityGuard 1.1 A : monitor() 1.1 B : monitor() :FireSensor :Siren 1.1.1 B *[(!intruder) & (!fire)] : intruder = isT riggered() sd ActivateAll 1.2 B : intruder() 1.2 A : fire() 1.3 A : soundFireAlarm() 1.3 B : soundIntruderAlarm() :SecuritySensor Рис. 20.11. Коммуникационная диаграмма с двумя параллельными потоками управления 460 Глава 20. Реализация прецедента на этапе проектирования На рис. 20.12 показана подсистема Customer с единственным интерфей сом CustomerManager. Рисунок 20.13 является частью диаграммы последовательностей, пред ставляющей взаимодействие актера с данной подсистемой. Обратите внимание, как изображен интерфейс: в прямоугольнике, примыкаю щем снизу к пиктограмме подсистемы. Поскольку интерфейс является частью подключаемой подсистемы, он может иметь собственную ли нию жизни, и сообщения могут направляться непосредственно к этой линии жизни. 20.7. Временные диаграммы Одним из слабых мест UML 1 было моделирование систем реального времени. Это такие системы, в которых временные соотношения кри тически важны и события должны следовать друг за другом в рамках определенного временного окна. Мы говорим «временное окно», а не «время», потому что абсолютное время для нас как разработчиков неприемлемо . Когда в модели задается время, на самом деле задается время плюс минус некоторая погрешность, определяемая внешними факторами, такими как точность системных часов. Обычно это не яв ляется проблемой, за исключением систем с очень точными временны ми ограничениями. Временные диаграммы моделируют временные ограничения. В UML 1 временные ограничения можно было обозначать на разных диаграммах, но не было отдельной диаграммы, предназначенной имен «subsystem» Customer CustomerManager Рис. 20.12. Подсистема Customer :Sales Agent «subsystem» :Customer getCustomerDetails( cid ) customerDetails :CustomerManager Рис. 20.13. Диаграмма взаимодействия актера с подсистемой Customer 20.7. Временные диаграммы 461 но для моделирования временных соотношений. UML 2 предоставляет разработчикам моделей систем реального времени временные диаграм мы. Это разновидность диаграммы взаимодействий, основное внима ние в которой направлено на моделирование временных ограничений. Таким образом, она идеально подходит для моделирования этого ас пекта систем реального времени. Временные диаграммы, аналогичные временным диаграммам UML, в течение многих лет успешно использу ются в электронной промышленности для моделирования временных ограничений электронных схем. Временные диаграммы очень просты. Время откладывается на гори зонтальной оси слева направо. Линии жизни и их состояния (или оп ределенные условия, накладываемые на линии жизни) располагаются вертикально. Переходы между состояниями линий жизни и условия ми представляются в виде графика. На рис. 20.14 приведена простая временная диаграмма для класса Siren. Эта диаграмма иллюстрирует, что происходит, когда формируется событие «вторжение», а затем со бытие «пожар». Конечно, это самый пессимистичный сценарий, но смоделировать его важно, чтобы понять взаимодействие функций об наружения взломщика и пожара. Вот последовательный анализ данной временной диаграммы. t = 0: :Siren находится в состоянии Off (выключен). t = 10: Происходит событие intruder, и :Siren переходит в состояние SoundingIntruderAlarm (воспроизведение сигнала тревоги втор жения). t = 25: Сигнал тревоги вторжения может звучать не более 15 минут согласно местным правилам подачи сигналов тревоги. :Siren sd IntruderThenFire SoundingFireAlarm SoundingIntruderAlarm Off :S iren {t <= 15} состояние или условие линия жизни intruder intruder fire cобытие ограничение продолжительности Resting {t = 30} 0 10 20 30 40 50 время в минутах временная линейка 60 70 80 90 100 intruder Рис. 20.14. Временная диаграмма для класса Siren 462 Глава 20. Реализация прецедента на этапе проектирования переходит в состояние Resting (покой). Он должен находиться в этом состоянии 30 минут (опять же по местным законам). t = 35: Происходит событие intruder, но :Siren в состоянии Resting, по этому не может воспроизводить сигнал тревоги. t = 55: :Siren возвращается в состояние Off. t = 65: Возникает еще одно событие intruder. :Siren переходит из со стояния Off в состояние SoundingIntruderAlarm. t = 75: Происходит событие fire. :Siren переходит из состояния Sound ingIntruderAlarm в состояние SoundingFireAlarm (воспроизведение сигнала пожарной тревоги). t = 100: Взаимодействие завершается, :Siren остается в состоянии SoundingFireAlarm. Временные диаграммы также можно представить в более компактной форме, когда состояния располагаются горизонтально. На рис. 20.15 в такой форме показана временная диаграмма с рис. 20.14. В такой компактной форме акцент обычно смещается больше на со стояния и относительное время, а не на представление абсолютного времени, как это моделирует временная линейка. Временные диаграммы могут использоваться для представления изме нений состояния объекта во времени. Временные диаграммы также могут использоваться для иллюстрации временных ограничений во взаимодействиях между двумя или более линиями жизни. На рис. 20.16 показано взаимодействие между лини ями жизни :FireSensorMonitor, :IntruderSensorMonitor и :Siren. На этой временной диаграмме следует отметить несколько интересных моментов: |