3. Правила и рекомендации по разработке диаграмм состояний.
4. Примеры построения диаграммы состояний.
Вопросы для самопроверки.
1. Способы детализации вариантов использования
После создания одной или нескольких диаграмм вариантов использования системный аналитик с заказчиком определяют приоритетность проработки вариантов использования и детализируют их. Главная цель данной процедуры – поиск ответа на вопрос: «В процессе какого поведения система обеспечит необходимую функциональность?».
В UML имеется несколько видов диаграмм, позволяющих детализировать варианты использования, – это диаграммы поведения. В связи с этим могут использоваться разные способы детализации:
- с помощью диаграмм состояний;
- с помощью диаграмм деятельности (аналог блок-схем);
- с помощью диаграмм взаимодействия (кооперации и последовательности).
2. Назначение и состав диаграммы состояний
Диаграммы состояний используются для описания поведения, реализуемого в рамках варианта использования, или поведения экземпляров класса (объектов), компонента, узла или системы в целом . Поведение моделируется через автомат (англ. state machine), описывающий возможные последовательности состояний экземпляра сущности и переходы между ними на протяжении его жизненного цикла, начиная от создания и заканчивая уничтожением.
Диаграмма состояний (автомат) представляет собой связный ориентированный граф, вершинами которого являются состояния, а дуги служат для обозначения переходов из состояния в состояние. Под состоянием (англ. state) понимается ситуация в ходе жизни экземпляра сущности, когда эта ситуация удовлетворяет некоторому условию, экземпляр выполняет некоторые операции или ждет наступления некоторого события. Например, для объекта его состояние может быть задано в виде набора конкретных значений атрибутов, при этом изменение этих значений будет отражать изменение состояния моделируемого объекта.
В UML различают два вида операций: действие и деятельность. Действие (англ. action) – это атомарная операция, выполнение которой не может быть прервано, приводящая к смене состояния или возвращающая значение. Примерами действий служат операции создания или уничтожения объекта, расчет факториала и т. д. Деятельность (англ. activity) – это составная (неатомарная) операция, реализуемая экземпляром в конкретном состоянии, выполнение которой может быть прервано. В частности, под деятельностью можно понимать процедуры расчета допускаемых скоростей или шифрования данных.
Событие (англ. event) – это спецификация существенного факта, который занимает некоторое положение во времени и в пространстве. В контексте диаграмм состояний событие – это спецификация факта, который может привести к смене состояния. События могут быть внутренними или внешними. Внешние события передаются между системой и актерами (например, нажатие кнопки или посылка сигнала от датчика передвижений). Внутренние события передаются между объектами внутри системы. В UML можно моделировать четыре вида событий:
- сигналы;
- вызовы;
- истечение промежутка времени;
- изменение состояния.
Сигнал (англ. signal) – спецификация факта посылки асинхронного сообщения между объектами. Исключения, которые поддерживаются в большинстве современных языков программирования, являются наиболее распространенным видом внутренних сигналов.
Вызов (англ. call) – спецификация факта посылки синхронного сообщения между объектами, предписывающего выполнение операции (действия или деятельности) объектом, которому посылается сообщение. Синхронность означает, что после посылки вызова объект-отправитель передает управление объекту-получателю и после выполнения последним операции получает управление обратно. Например, закрасить фигуру красным фоном fill(red) или рассчитать допускаемые скорости calculateVdop().
Событие времени – спецификация факта, обозначающего истечение промежутка времени с момента входа в текущее состояние. В UML данный факт специфицируется с помощью ключевого слова «after» (англ. – после). Например, after(2 seconds).
Событие изменения состояния – спецификация логического (булевского) условия. В контексте диаграмм состояний данное событие приводит к изменению состояния экземпляра сущности. В UML оно специфицируется с помощью ключевого слова «when» (англ. – когда) или сторожевого условия. Например, when(A < B) или [A < B].
Переход (англ. transition) – отношение между двумя состояниями, показывающее возможный путь изменения состояния экземпляра сущности.
Состояние отображается в виде прямоугольника со скругленными углами, внутри которого записывается имя (рис..1). Рекомендуется в качестве имени использовать глаголы в настоящем времени (звенит, печатает, ожидает) или причастия (занято, передано, получено).
Рис. 1. Способы отображения состояний
Характеристика состояния может содержать описание выполняемых операций, перед которыми указывается одна из стандартных меток:
- entry (англ. – вход) – действие при входе, выполняемое вне зависимости от того, по какому переходу был выполнен вход в состояние. Например, создать соединение с базой данных entry / createConnect();
- exit (англ. – выход) – действие при выходе, выполняемое вне зависимости от того, по какому переходу был выполнен выход из состояния. Например, закрыть соединение с базой данных exit / closeConnect();
- do (англ. – выполнять) – деятельность в состоянии. Находясь в состоянии, объект может бездействовать и ждать наступления некоторого события, а может выполнять длительную операцию. Например, рассчитать допускаемые скорости do / calculateVdop(). Допускается указывать несколько операций в виде отдельных строк, каждая из которых начинается с метки do, или в виде одной строки, операции в которой отделены друг от друга точкой с запятой;
- newTarget (англ. – новое задание) – внутренний переход, предписывающий обработку новых событий, не покидая текущего состояния. При выполнении внутреннего перехода повторно не выполняются действия при входе или выходе из состояния. Например, временная остановка (прерывание) расчета допускаемых скоростей, newTarget / pauseCalculateVdop();
- defer (англ. – отложить) – отложенное событие, обработка которого предписывается в другом состоянии, но после того, как все операции в текущем будут завершены. Например, отображение на экране сообщения об ошибках в исходных данных defer / showDataError().
На рис. 2 показан пример состояния с характеристикой.
Рис. 2. Пример состояния
Допускается определять в характеристике собственные метки.
В UML определены два специальных псевдосостояния: начальное и конечное (рис. 3).
Рис. 3. Начальное и конечное состояния:
а – начальное состояние; б – конечное состояние
Начальное состояние (англ. start state) – состояние, в котором находится экземпляр сущности после своего создания или, перейдя в составное состояние. Из начального состояния могут только исходить переходы. Конечное состояние (англ. final state) – состояние, обозначающее факт уничтожения экземпляра сущности или выхода из составного состояния. В конечное состояние могут только входить переходы.
Переход отображается в виде однонаправленной ассоциации между двумя состояниями. При смене состояний говорят, что переход срабатывает. До срабатывания перехода экземпляр сущности находится в состоянии, называемом исходным, а после его срабатывания – в целевом.
Различают два вида переходов: нетриггерный и триггерный. Переход первого вида, называемый также переходом по завершении, срабатывает неявно, когда все основные операции (с метками entry, do и exit) в исходном состоянии успешно завершают свою работу. Данный вид перехода обозначается стрелкой без надписи. Для наступления триггерного перехода необходимо наступление некоторого события, которое записывается над стрелкой. В общем случае над стрелкой может быть записана строка текста вида «событие / действие». Указываемое действие представляет собой атомарную операцию, выполняемую сразу после срабатывания соответствующего перехода и до начала каких бы то ни было операций в целевом состоянии. Разрешается указывать не одно, а несколько обособленных действий, отделенных друг от друга точкой с запятой. Обязательное требование – все действия в списке должны четко различаться между собой и следовать в порядке их записи. Примеры спецификации переходов:
- mouseClick() – нажатие кнопки мыши в момент, когда указатель находится над моделируемым объектом (например, над командной кнопкой, запускающей процедуру определения допускаемых скоростей);
- mouseClick() / setFocus() – нажатие кнопки мыши с одновременным установлением фокуса на моделируемом объекте;
- mouseClick() [isEnabled() = true] / setFocus() – нажатие кнопки мыши с одновременным установлением фокуса на моделируемом объекте при условии, что он доступен. В данном примере объект переходит в новое состояние только в том случае, если произойдет событие и сторожевое условие будет истинно. Вычисление истинности сторожевого условия происходит только после возникновения ассоциированного с ним события, инициирующего соответствующий переход. Если событие не произошло или сторожевое условие ложно, то переход не срабатывает, и действие, записанное через «/», не выполняется;
- when(14:00) или [getTime() = 14:00] – текущее время на компьютере равно 14 часам;
- при построении концептуальных диаграмм допускается обозначать переход произвольной строкой текста, характеризующей событие. Например, «столкновение» или «выход из строя».
Переход может быть направлен в то же состояние, из которого он выходит. Такой переход называется рефлексивным. В отличие от внутренних переходов, при рефлексивном переходе выполняются внутренние действия, ассоциированные с метками entry и exit.
На рис. 4 показаны простой и рефлексивный переходы.
Рис. 4. Простой (а) и рефлексивный (б) переходы
На диаграмме могут отображаться составные состояния (англ. composite state), состоящие из вложенных в них подсостояний (англ. substate) (рис. 5).
Рис. 5. Составное состояние и подсостояния
Подсостояния отображаются внутри составного состояния (подавтомата).
Составное состояние может содержать параллельные подавтоматы или последовательно выполняемые подсостояния. При этом любое из подсостояний в свою очередь может являться составным состоянием.
Последовательные подсостояния (англ. sequential substates) используются для моделирования такого поведения экземпляра сущности, во время которого в каждый момент времени он находится в одном, и только одном, подсостоянии. Поведение экземпляра сущности в этом случае представляет собой последовательную смену подсостояний от начального до конечного. Хотя сущность продолжает находиться в составном состоянии, введение в рассмотрение последовательных подсостояний позволяет отобразить более тонкие логические аспекты его внутреннего поведения.
Параллельные подсостояния (англ. concurrent substates) позволяют специфицировать два и более подавтомата, которые могут параллельно выполняться внутри составного события. Каждый из подавтоматов занимает некоторую область внутри составного состояния, которая отделяется от остальных горизонтальной линией (рис. 6). Если на диаграмме имеется составное состояние с вложенными параллельными подавтоматами, то экземпляр сущности может одновременно находиться в нескольких подсостояниях, но не более чем по одному из каждого подавтомата. Если какой-либо из подавтоматов пришел в свое конечное состояние раньше других, то он должен ожидать, пока другие подавтоматы не придут в свои конечные состояния.
Рис. 6. Составное состояние с вложенными параллельными подавтоматами
В некоторых случаях бывает желательно скрыть внутреннюю структуру составного состояния. Например, отдельный подавтомат может быть настолько большим по масштабу, что его визуализация затруднит общее представление диаграммы состояний. В подобной ситуации допускается не раскрывать на исходной диаграмме данное составное состояние, а указать в правом нижнем углу специальный символ (рис. 7).
Рис. 7. Пример составного состояния со скрытой внутренней структурой
В последующем диаграмма состояний для соответствующего подавтомата может быть изображена отдельно от основной с необходимыми комментариями. Таким образом при построении диаграмм состояний могут использоваться классические принципы декомпозиции и иерархического упорядочения.
Современные программы могут реализовывать очень сложную логику поведения отдельных своих компонентов. Для адекватного представления процесса изменения состояний семантики обычных простых переходов может оказаться недостаточно. С этой целью в UML специфицированы дополнительные обозначения для отображения сложных переходов.
В отдельных случаях переход может иметь несколько исходных или несколько целевых подсостояний. Такой переход получил название параллельный и может отображаться только для подсостояний параллельных подавтоматов. Графически такой переход изображается вертикальной или горизонтальной чертой (рис. 8). Если параллельный переход имеет две или более входящих дуг, то его называют соединением (англ. join). Если же он имеет две или более исходящих из него дуг, то его называют ветвлением (англ. fork). Текстовая спецификация параллельного перехода записывается рядом с чертой и относится ко всем входящим (исходящим) дугам.
Рис. 8. Параллельные переходы
На рис. 8 переход из состояния 1 выполняется параллельно в подсостояния 1 и 4, в то время как переход из состояния 2 – в подсостояния 1 и 3.
Переходы в подсостояния или из них могут обладать еще более сложной семантикой. Так, на рис. 9 отображены следующие варианты переходов (спецификация переходов не показана):
- переход в конкретное подсостояние (переход из состояния «Выбор участка» строго в начальное подсостояние);
- выход из конкретного подсостояния (переходы из подсостояний «Формирование задания» и «Расчет» в состояние «Корректировка исходных данных»);
- переход, стрелка которого соединена с границей некоторого составного состояния, обозначает переход в начальное подсостояние податомата (переход из состояния «Корректировка исходных данных»). Если внутри составного состояния имеется знак истории состояния (англ. state history), то переход выполняется в подсостояние, из которого в последний раз был выполнен выход из составного состояния, несмотря на начальное состояние. Так в примере, если после корректировки данных впервые выполняется определение скоростей, то по переходу система попадает в начальное подсостояние. Если в подсостояниях «Формирование задания» и «Расчет» обнаруживается необходимость корректировки исходных данных, то возврат управления осуществляется в вызвавшее корректировку подсостояние;
- переход, стрелка которого выходит из границы некоторого составного состояния, обозначает переход из его конечного подсостояния (переход в конечное состояние).
Рис. 9. Сложные переходы
Как уже было отмечено, поведение параллельных подавтоматов независимо друг от друга, что позволяет реализовать многозадачность в программных системах. Однако в некоторых случаях может возникнуть необходимость учета в модели синхронизации наступления отдельных событий. Для этой цели в UML имеется специальное псевдосостояние, которое называется синхронизирующим (англ. synch state).
Синхронизирующее состояние обозначается небольшой окружностью, внутри которой помещен символ звездочки «*». Оно используется совместно с переходом-соединением или переходом-ветвлением для того, чтобы явно указать события в других подавтоматах, оказывающие непосредственное влияние на поведение данного подавтомата. На рис. 10 показан пример использования синхронизирующих состояний.
Рис. 10. Диаграмма состояний с синхронизирующими состояниями
Данный пример иллюстрирует работу двух бригад при постройке дома. Очевидно, что некоторые работы, выполняемые одной бригадой, должны начинаться после завершения работ другой и наоборот.