В UML несколько переходов ведут в одно состояние, и при каждом входе в него нужно выполнить одно и то же действие. Где его моделировать, чтобы не дублировать на переходах?
Действие следует моделировать как entry-действие состояния. Оно автоматически выполняется при каждом входе в это состояние независимо от того, какой переход был выбран.
Это лучше, чем дублировать действие на всех входящих переходах: модель становится короче, а изменение поведения нужно вносить только в одном месте.
UML-диаграммы состояний предназначены для описания поведения объекта через состояния, события и переходы. На практике одно состояние часто достигается из нескольких разных сценариев, поэтому действие, общее для всех входов, неудобно размещать на каждом переходе отдельно.
Для таких случаев в UML предусмотрены действия, связанные с жизненным циклом состояния: entry выполняется при входе, doActivity — во время нахождения в состоянии, exit — при выходе.
Если общее действие записать на каждом входящем переходе, возникает риск рассинхронизации. Один из переходов могут изменить или дополнить, но разработчик забудет обновить остальные.
Неверное размещение также искажает смысл модели. Действие на переходе означает, что оно относится к конкретному событию и конкретному маршруту, а entry-действие выражает инвариант входа: оно должно выполняться при достижении состояния любым допустимым путем.
Важно не путать действие с guard-условием. Guard только проверяет возможность перехода и не должен использоваться для выполнения операций или изменения данных.
Действие, обязательное при каждом входе в состояние, размещают как entry action этого состояния. Семантически сначала выбирается и выполняется переход, затем объект входит в целевое состояние и запускает его entry-действие.
Если состояние достигнуто повторно после выхода из него, entry-действие выполняется снова. При внутреннем событии, которое не вызывает выхода из состояния, entry-действие обычно не запускается повторно: оно связано именно с входом, а не с любой активностью внутри состояния.
Размещение на переходе оправдано, когда действие специфично для конкретного маршрута. Например, разные переходы могут входить в одно состояние, но только один из них должен записывать причину возврата. В таком случае действие не является общим entry-поведением.
Если действие должно выполняться после завершения входной обработки и продолжаться независимо от конкретных событий, можно рассмотреть doActivity. Если оно должно выполняться непосредственно перед выходом из состояния, подходит exit action.
В системе обработки заявок состояние «На проверке» достигается после ручной передачи заявки и после автоматического возврата на повторную проверку. В обоих случаях нужно зафиксировать момент начала проверки и назначить контрольный срок.
Вариант с копированием действий на двух переходах прост на старте, но создаёт риск расхождения: например, срок будет назначаться только для ручной передачи. Вариант с отдельным промежуточным состоянием устраняет дублирование, но усложняет диаграмму и добавляет искусственный элемент процесса.
Выбран entry-переход состояния «На проверке», поскольку действия являются свойством самого входа в состояние. В результате оба сценария используют единую семантику, а изменение правил запуска проверки выполняется централизованно.
Действие на переходе относится к конкретному переходу между состояниями и может выполняться только при определённом событии. Entry-действие относится к целевому состоянию и выполняется при каждом входе в него, каким бы переходом вход ни был вызван.
Поэтому выбор зависит от области применимости действия: общий обязательный шаг входа моделируют на состоянии, маршрутно-зависимый шаг — на переходе.
Обычно нет, если внутреннее событие не вызывает выхода из состояния и повторного входа в него. Entry-действие связано с жизненным циклом входа, а не с самим фактом обработки любого события.
Если бизнес-правило требует запускать действие при каждом событии, это следует моделировать отдельно — например, действием перехода или обработкой события внутри состояния. Нельзя автоматически считать такое поведение entry-семантикой.
Guard применяют для выбора или разрешения перехода: он должен вернуть логический результат и не заменяет действие входа. Размещение операции в guard смешивает проверку условия с побочными эффектами и может сделать поведение модели непредсказуемым при повторной оценке условия.
Если нужно проверить возможность входа, используют guard на переходе. Если после разрешённого входа нужно выполнить действие, его размещают в entry-секции целевого состояния.