На этом шаге рассмотрим последовательные подсостояния в диаграмме состояний в UML.
Рассмотренные свойства состояний и переходов решают целый ряд типичных проблем моделирования автоматов. Однако у автоматов, рассматриваемых в UML, есть свойство, которое позволяет еще больше упростить моделирование сложного поведения, – подсостояние (substate), то есть такое состояние, которое входит в состав другого. Например, Heater (Обогреватель) может находиться в состоянии Heating (Обогрев) и в то же время – во вложенном состоянии Activating (Активация). В этом случае правильно будет сказать, что объект находится одновременно в состояниях Heating и Activating.
Простое состояние – такое, которое не имеет внутренней структуры. Состояние, имеющее вложения, то есть подсостояния, называется составным (композитным). Подсостояния могут быть параллельными (ортогональными) или последовательными (неортогональными). В UML составное состояние изображается так же, как и простое, но с дополнительным разделом, в котором показан вложенный автомат. Глубина вложения состояний не ограничена.
Неортогональные (последовательные) подсостояния
Рассмотрим моделирование поведения банкомата. Система обслуживания может находиться в одном из трех основных состояний: Idle (Простой – ожидание взаимодействия с пользователем), Active (Активен – выполняет транзакцию, запрошенную пользователем) либо Maintenance (Обслуживание – возможно, пополняется запас наличности). В состоянии Active поведение банкомата описывается простой схемой: проверить счет пользователя, выбрать тип транзакции, выполнить транзакцию, напечатать чек. После этого банкомат снова возвращается в состояние Idle. Перечисленные этапы поведения можно представить как состояния Validating (Проверка), Selecting (Выбор), Processing (Обработка), Printing (Печать). Может быть, желательно настроить систему таким образом, чтобы она давала пользователю право выбрать и осуществить несколько транзакций после того, как выполнена проверка счета, но до печати общего чека.
Проблема в том, что на любом этапе у пользователя должна быть возможность отменить транзакцию и вернуть банкомат в состояние Idle. Такого эффекта можно достичь, используя простые автоматы, но это будет не очень красиво. Поскольку пользователь может отменить транзакцию в любой момент времени, пришлось бы включать соответствующий переход из любого состояния в последовательности Active. Это чревато ошибками, так как предусмотреть все необходимые переходы непросто, а наличие множества событий, прерывающих основной поток управления, приведет к появлению большого числа переходов из разных исходных состояний, которые будут оканчиваться в одном и том же целевом. При этом у каждого из них будут одни и те же инициирующее событие, защитное условие и действие.
При помощи вложенных подсостояний можно упростить моделирование этой задачи, как показано на рис. 1.
Рис.1. Последовательные подсостояния
Здесь у состояния Active есть внутренний автомат, в который входят подсостояния Validating, Selecting, Processing, Printing. Банкомат переходит из состояния Idle в состояние Active, когда пользователь вставляет в картоприемник кредитную карту. При входе в состояние Active выполняется действие readCard (читатьКарту). Начав с исходного состояния внутреннего автомата, управление переходит к состоянию Validating, затем к Selecting и, наконец, к Processing. После выхода из последнего управление может вернуться в состояние Selecting (если пользователь избрал другую транзакцию) или перейти в состояние Printing. Отсюда происходит безусловный завершающий переход в состояние Idle. Отметим, что у состояния Active есть действие выхода, которое обеспечивает автоматическое извлечение кредитной карты.
Также следует обратить внимание на переход из состояния Active в состояние Idle, инициируемый событием cancel (отмена). В любом подсостоянии состояния Active пользователь может отменить транзакцию и вернуть банкомат в состояние простоя (но только после извлечения кредитной карты из картоприемника – это действие, сопровождающее выход из состояния Active, происходит независимо от того, что послужило причиной выхода). Без подсостояний потребовался бы переход, инициируемый событием cancel, для каждого состояния подструктуры.
Такие подсостояния, как Validating и Processing, называются неортогональными (nonorthogonal), или непересекающимися. При наличии ряда неортогональных подсостояний в контексте включающего составного состояния объект может находиться одновременно в этом составном состоянии и только в одном из его подсостояний (или в конечном состоянии). Таким образом, неортогональные подсостояния разделяют пространство составного состояния на непересекающиеся состояния.
Переход от исходного состояния, внешнего по отношению к составному включающему состоянию, может вести либо к этому последнему, либо к одному из его подсостояний. Если целью перехода является составное состояние, то вложенный автомат должен иметь начальное состояние, которому передается управление после входа в составное состояние и после выполнения его входного действия, если таковое присутствует. Если цель перехода – вложенное подсостояние, то управление передается ему после выполнения входного действия (если оно есть) составного состояния и (если оно есть) входного действия этого целевого подсостояния.
Переход, ведущий из составного состояния, может иметь в качестве исходного либо его само, либо одно из его подсостояний. В любом случае управление сначала покидает вложенное состояние (при этом выполняется его выходное действие, если таковое присутствует), а затем составное состояние (при этом выполняется его выходное действие, если оно есть). Переход, началом которого является составное состояние, по существу, прерывает деятельность вложенного автомата. Завершающий переход составного состояния выполняется, когда управление достигает его конечного подсостояния.
Вложенные неортогональные автоматы могут иметь как максимум одно начальное подсостояние и одно конечное.
На следующем шаге рассмотрим исторические состояния конечного автомата.