Государственный автомат представительства

Я хочу реализовать GUI как конечный автомат. Я думаю, что есть некоторые преимущества и недостатки этого, но это не тема этих вопросов.

После некоторого прочтения об этом я нашел несколько способов моделирования конечного автомата в C ++ и остановился на 2, но я не знаю, какой метод может лучше подойти для моделирования GUI.

  1. Представьте конечный автомат в виде списка состояний с помощью следующих методов:

    • OnEvent(...);
    • OnEnterState(...);
    • OnExitState(...);

    От StateMachine::OnEvent(...) Я пересылаю мероприятие на CurrentState::OnEvent(...) и здесь принимается решение сделать переход или нет. На переходе звоню CurrentState::OnExitState(...), NewState::OnEnterState() а также CurrentState = NewState;

    При таком подходе государство будет тесно связано с действиями, но State может усложниться, когда из одного состояния я могу перейти в несколько состояний, и мне придется выполнять разные действия для разных переходов.

  2. Представлять конечный автомат в виде списка переходов со следующими свойствами:

    • InitialState
    • FinalState
    • OnEvent(...)
    • DoTransition(...)

    От StateMachine::OnEvent(...) Я пересылаю событие на все переходы, где InitialState имеет то же значение, что и CurrentState в состоянии машины. Если условие перехода выполнено, цикл останавливается, DoTransition метод называется и CurrentState установлен в Transition::FinalState,

    С таким подходом Transition будет очень просто, но число переходов может стать очень большим. Также станет сложнее отслеживать, какие действия будут выполнены, когда одно государство получит событие.

Какой подход, по вашему мнению, лучше для моделирования GUI. Знаете ли вы другие представления, которые могут быть лучше для моей проблемы?

3

Решение

Вот третий вариант:

  • Представлять конечный автомат в качестве матрицы перехода
    • Индекс матрицы столбца представляет состояние
    • Индекс строки матрицы представляет собой symbol (увидеть ниже)
    • Ячейка матрицы представляет собой состояние машины, в которое необходимо перейти. Это может быть как новое состояние, так и одно и то же.
    • В каждом штате есть OnEvent метод, который возвращает symbol

От StateMachine::OnEvent(...) события направляются State::OnEvent который возвращает symbol — результат исполнения. 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

Суть этого бытия:

  1. Количество symbols будет, вероятно, намного ниже, чем у штатов.
  2. Штаты не знают друг друга
  3. Все переходы управляются из одной точки, поэтому в тот момент, когда вы хотите обработать символ DB_ERROR иначе NETWORK_ERROR вы просто меняете таблицу переходов и не трогаете реализацию состояний.
3

Другие решения

Я не знаю, ожидаете ли вы такого ответа, но я использую, чтобы иметь дело с такими конечными автоматами прямым способом.

Используйте переменную состояния перечислимого типа (возможные состояния). В каждом обработчике событий GUI проверьте значение состояния, например, с помощью оператора switch. Выполните все необходимые обработки, соответственно, и установите следующее значение состояния.

Легкий и гибкий. Поддержание регулярности кода делает его читабельным и «формальным».

1

Я лично предпочел бы первый метод, который вы сказали. Я нахожу второй довольно нелогичным и чрезмерно сложным. Наличие одного класса для каждого состояния просто и легко, если затем вы установите правильные обработчики событий в OnEnterState и удалите их в OnExitState, ваш код будет чистым, и все будет содержаться в соответствующем состоянии, что позволяет легко читать.

Вы также избежите использования огромных операторов switch для выбора правильного обработчика событий или процедуры для вызова, поскольку все, что делает состояние, прекрасно видно внутри самого состояния, что делает код конечного автомата коротким и простым.

И последнее, но не менее важное, этот способ кодирования — это точный перевод от рисования конечного автомата на любой язык, который вы будете использовать.

1

Я предпочитаю действительно простой подход для такого рода кода.

  • Перечень состояний.
  • Каждый обработчик событий проверяет текущее состояние, прежде чем решить, какое действие предпринять. Действия — это просто составные блоки внутри switch заявление или if цепь и установить следующее состояние.
  • Когда действия становятся длиннее нескольких строк или требуют повторного использования, рефакторинг как вызовы отдельных вспомогательных методов.

Таким образом, нет никаких дополнительных структур метаданных управления конечным автоматом и нет кода для управления этими метаданными. Только ваши бизнес-данные и логика перехода. А действия могут напрямую проверять и изменять все переменные-члены, включая текущее состояние.

Недостатком является то, что вы не можете добавить дополнительные элементы данных, локализованные в одном состоянии. Что не является реальной проблемой, если у вас действительно большое количество штатов.

Я считаю, что это также приводит к более надежной конструкции, если вы всегда настраиваете все атрибуты пользовательского интерфейса при входе в каждое состояние, вместо того, чтобы делать предположения о предыдущем параметре и создавать поведения выхода-состояния для восстановления инвариантов перед переходами состояний. Это применимо независимо от того, какую схему вы используете для реализации переходов.

0

Вы также можете рассмотреть возможность моделирования желаемого поведения с помощью сети Петри. Это было бы предпочтительным, если вы хотите реализовать более сложное поведение, поскольку оно позволяет вам точно определить все возможные сценарии и предотвратить взаимные блокировки.

Эта библиотека может быть полезна для реализации конечного автомата для управления вашим графическим интерфейсом: Двигатель PTN

0
По вопросам рекламы [email protected]