Представление государственной машины
Я хочу реализовать графический интерфейс как государственную машину. Я думаю, что есть некоторые преимущества и некоторые недостатки в этом, но это не тема этих вопросов.
После некоторого чтения об этом я нашел несколько способов моделирования машины состояний в C++ и остановился на 2, но я не знаю, какой метод может лучше подходить для моделирования GUI.-
Представить государственную машину в виде списка состояний со следующими параметрами: методы:
OnEvent(...);
OnEnterState(...);
OnExitState(...);
Из
При таком подходе состояние будет тесно связано с действиями, ноStateMachine::OnEvent(...)
я переправляю событие вCurrentState::OnEvent(...)
и здесь принимается решение сделать переход или нет. По переходу звонюCurrentState::OnExitState(...)
,NewState::OnEnterState()
иCurrentState = NewState;
State
может усложниться, когда из одного состояния я могу перейти в несколько состояний, и мне придется выполнять различные действия для разных переходов. -
Представляют собой государственная машина как список переходов со следующими свойствами:
InitialState
FinalState
OnEvent(...)
DoTransition(...)
Из
StateMachine::OnEvent(...)
я передаю событие всем переходам, гдеInitialState
имеет то же значение, что иCurrentState
в государственной машине. Если условие перехода выполнено, цикл останавливается, вызывается методDoTransition
иCurrentState
устанавливается вTransition::FinalState
.При таком подходе
Transition
будет очень просто, но количество переходов может оказаться очень большим. Кроме того, станет труднее отслеживать, какие действия будут сделаны, когда одно государство получает событие.
Какой подход, по вашему мнению, лучше подходит для моделирования графического интерфейса. Знаете ли вы другие представления, которые могут быть лучше для моей проблемы?
5 ответов:
Вот третий вариант:
- представить государственную машину в виде матрицы переходов
- индекс столбца матрицы представляет состояние
- индекс строки матрицы представляет собой
Ячейка матрицы представляет состояние, в которое должен перейти мачихе. Это может быть как новое состояние, так и одно и то же состояниеsymbol
(см. ниже)- каждое состояние имеет метод
OnEvent
, который возвращаетsymbol
Из
StateMachine::OnEvent(...)
события передаются вState::OnEvent
, который возвращает asymbol
- a результат исполнения.StateMachine
затем на основе текущего состояния и возвращенного символа решает, является ли
- переход в другое состояние должен быть сделан, или
- текущее состояние сохраняется
- необязательно, если переход совершен,
OnExitState
иOnEnterState
вызывается для соответствующих состоянийПример матрицы для 3 состояний и 3 символов
0 1 2 1 2 0 2 0 1
В этом примере, если машина находится в любом из состояний
Вторая строка говорит, что если текущее состояние(0,1,2)
иState::OnEvent
возвращает символ0
(первая строка в матрице) - он остается в том же состоянии0
и возвращаемый символ1
, то переход осуществляется в состояние1
. Для состояния1
- > состояние2
и для состояния2
- > состояние0
.Аналогично третьей строке написано, что за символ
2
, гос.0
-> государство2
, гос.1
-> государство0
, гос.2
-> государство1
Суть этого бытия:
- число
symbols
, вероятно, будет намного меньше, чем число состояния.- государства не осознают друг друга
- все переходы управляются из одной точки, поэтому в тот момент, когда вы хотите обработать символ
DB_ERROR
иначе, чемNETWORK_ERROR
, вы просто меняете таблицу переходов и не касаетесь реализации состояний.
Я не знаю, такого ли ответа вы ожидаете, но я использую, чтобы иметь дело с такими автоматами состояний простым способом.
Используйте переменную состояния перечисляемого типа (возможные состояния). В каждом обработчике событий графического интерфейса проверьте значение состояния, например, с помощью оператора switch. Сделайте любую обработку, которая там должна быть соответственно и установите следующее значение состояния.
Легкий и гибкий. Регулярное хранение кода делает его читаемым и "формальный".
Я лично предпочел бы первый метод, который вы сказали. Я нахожу второй вариант довольно противоречивым и чрезмерно сложным. Иметь один класс для каждого состояния просто и легко, если затем вы установите правильные обработчики событий в OnEnterState и удалите их в OnExitState, ваш код будет чистым, и все будет самодостаточно в соответствующем состоянии, что позволит легко читать.
Вы также избежите наличия огромных операторов switch для выбора правильного обработчика событий или процедура вызова, как и все, что делает состояние, прекрасно видна внутри самого состояния, что делает машинный код состояния коротким и простым.
И последнее, но не менее важное: этот способ кодирования представляет собой точный перевод с языка state machine draw на любой язык, который вы будете использовать.
Я предпочитаю действительно простой подход для такого рода кода.
- перечисление состояний.
- каждый обработчик событий проверяет текущее состояние, прежде чем решить, какое действие предпринять. Действия - это просто составные блоки внутри оператора
switch
или цепочкиif
, которые задают следующее состояние.- Когда действия становятся длиннее пары строк или нуждаются в повторном использовании, рефакторинг выполняется как вызовы отдельных вспомогательных методов.
Таким образом, нет никакого дополнительного управления государственной машиной структуры метаданных и отсутствие кода для управления этими метаданными. Только ваши бизнес-данные и логика перехода. А действия могут непосредственно проверять и изменять все переменные-члены, включая текущее состояние.
Недостатком является то, что вы не можете добавить дополнительные элементы данных, локализованные в одном состоянии. Что не является реальной проблемой, если у вас нет действительно большого количества состояний.
Я нахожу, что это также приводит к более надежному дизайну, если вы всегда настраиваете все атрибуты пользовательского интерфейса при входе в каждое состояние, вместо того, чтобы делать предположения о предыдущей установке и создавать поведение выхода из состояния для восстановления инвариантов перед переходами состояний. Это применимо независимо от того, какую схему вы используете для реализации переходов.
Можно также рассмотреть возможность моделирования желаемого поведения с помощью сети Петри. Это было бы предпочтительнее, если вы хотите реализовать более сложное поведение, так как оно позволяет точно определить все возможные сценарии и предотвратить тупики.
Эта библиотека может быть полезна для реализации машины состояний для управления вашим графическим интерфейсом: PTN Engine