Добрый день. В целях самообразования решил запилить очередной велосипед. Имеется конечный автомат (F), событие (E), текущее состояние конечного автомата (S), менеджер, принимающий события (M)
Пока что получается следущее взаимодействие:
- некоторая сущность кладёт в M событие E
- M по своим правилам (их можно опустить) посылает E в F
- F «пересылает» E своему текущему S
- S соответствующим образом обрабатывает E, после чего должен сообщить F в каком состоянии он должен остаться/перейти после того как обработает E.
Возникает вопрос по последнему пункту: есть ли паттерны/механизмы «подписки/регистрации» в базовом классе?
По идее я могу сделать так:
- Создать класс FSM под конкретную задачу
- Отнаследоваться от FSM_I
- Внутри FSM описать классы под каждые состояния и сделать по одному экземпляру
- Хранить адрес текущего состояния и изменять его
Но такой механизм мне не очень нравится, хочется решить как-то более красиво, наверняка такие способы есть.
#include <iostream>
#include <string>
class StateI
{
public:
virtual ~StateI() = default;
virtual StateI* proceed() = 0;
virtual std::string getName() = 0;
};
class FsmI
{
public:
virtual ~FsmI() = default;
virtual void proceed() = 0;
};
class Fsm
: public FsmI
{
public:
Fsm();
void proceed() override;
private:
class StateA : public StateI
{
public:
StateI* proceed() override;
std::string getName() override;
private:
int m_nCounter{0};
};
class StateB : public StateI
{
public:
StateI* proceed() override;
std::string getName() override;
};
static StateA m_stateA;
static StateB m_stateB;
StateI* m_currentState;
};
Fsm::StateA Fsm::m_stateA = Fsm::StateA();
Fsm::StateB Fsm::m_stateB = Fsm::StateB();
Fsm::Fsm()
{
m_currentState = &m_stateA;
}
void Fsm::proceed()
{
StateI* before = m_currentState;
StateI* after = m_currentState->proceed();
m_currentState = after;
std::cout << before->getName() << " -> " << after->getName() << std::endl;
}
StateI* Fsm::StateA::proceed()
{
++m_nCounter;
if (m_nCounter > 2)
return &m_stateB;
return this;
}
std::string Fsm::StateA::getName()
{
return "stateA";
}
StateI* Fsm::StateB::proceed()
{
return &m_stateA;
}
std::string Fsm::StateB::getName()
{
return "stateB";
}
int main(int argc, char** argv)
{
std::cout << std::endl << "---- f1 ----" << std::endl;
Fsm f1;
f1.proceed();
f1.proceed();
f1.proceed();
f1.proceed();
return 0;
}
Получаю следующий вывод:
---- f1 ----
stateA -> stateA
stateA -> stateA
stateA -> stateB
stateB -> stateA
Юзать можно что угодно (либу собираю с поддержкой C++17), но не готовые либы, хочется самому набить шишки (потом пойдёт многопоточность и т.п.).