Конечные автоматы, инкапсуляция и ООП дизайн
Я внедряю FSM, используя библиотеку Boost's MSM.
В FSM у меня есть таблицы переходов, которые описывают события, состояния источника, состояния цели, действия и охрану.
Я впервые использую высокоуровневый дизайн конечного автомата. В прошлом я только использовал параметры переключения и выполнял код. Тем не менее, этот будет большим, и я хотел, чтобы все было организовано должным образом, чтобы это не превратилось в беспорядок.
У меня есть объект, который представляет конечный автомат (это boost::msm::back::state_machine<MyStateMachine>
где MyStateMachine
моя реализация, которая наследует от boost::msm::front::state_machine_def
).
Хитрость в том, что моя бизнес-логика будет выполнена в Actions. Я не думаю, что это необычно для FSM. Примеры Boost, кажется, предполагают, что эти Действия должны быть реализованы как методы в самом автомате, но я думаю, что это может сделать один класс слишком массивным. Я чувствую, что имеет смысл отделить работу от государственной машины.
Что имеет больше смысла, чтобы сохранить читабельный, поддерживаемый и расширяемый дизайн?
Делайте бизнес-логику в методах класса FSM (я беспокоюсь, что это слишком смешивает управление состоянием и работу).
Выполните бизнес-логику в родительском объекте, который создает экземпляр FSM. ФСМ понадобится указатель на родительский элемент, а родительский должен будет реализовать интерфейс, понятный ФСМ (или реализация ФСМ должна будет # включать объявление родителя).
Выполните бизнес-логику в новом классе, который создается и принадлежит FSM.
Выполните бизнес-логику в новом классе, который создается и принадлежит родительскому объекту, но передается в качестве ссылки (или указателя) на FSM.
Что-то другое.
1 ответ
Это зависит от вашей ситуации, но у меня есть один подход, который я обычно использую.
Может быть, это вариация 2 или 5.
Скажем your_app
есть ваша бизнес-логика. И это должно вести себя как конечный автомат. Я думаю, что это один из типичных вариантов использования конечного автомата.
В этом случае конечный автомат может быть размещен как вложенный класс your_app
, your_app
имеет переменную-член sm_
Экземпляр конечного автомата.
Определение конечного автомата sm_def
, Он имеет ссылку на your_app
,
Когда кто-то за пределами your_app
хотите обработать событие, позвоните your_app::process_event()
, Если вы не хотите предоставлять прямой интерфейс обработки событий, вы можете обернуть его как your_app::handle_some()
, Если ты так сделаешь, your_app::process_event()
должен быть приватным.
Вот пример реализации:
#include <iostream>
#include <boost/msm/back/state_machine.hpp>
#include <boost/msm/front/state_machine_def.hpp>
#include <boost/msm/front/functor_row.hpp>
namespace msm = boost::msm;
namespace msmf = boost::msm::front;
namespace mpl = boost::mpl;
// application domain
class your_app {
public:
your_app() :sm_(boost::ref(*this)) {
sm_.start(); // start state machine
}
// public interface for event processing
// Event definitions
struct Event1 {
int param;
};
template <typename Event>
void process_event(Event const& ev) {
sm_.process_event(ev);
}
void handle_some(int param) {
process_event(Event1 {param});
}
private:
// internal business logic triggered from the state machine
void do_some_business(int param) {
std::cout << "do_some_business " << param << std::endl;
}
// state machine definiition
struct sm_def:msmf::state_machine_def<sm_def> {
sm_def(your_app& ya):ya_(ya) {}
// States
struct State1:msmf::state<> {
template <class Event,class Fsm>
void on_entry(Event const&, Fsm&) {
std::cout << "State1::on_entry()" << std::endl;
}
template <class Event,class Fsm>
void on_exit(Event const&, Fsm&) {
std::cout << "State1::on_exit()" << std::endl;
}
};
struct State2:msmf::state<> {
template <class Event,class Fsm>
void on_entry(Event const&, Fsm&) {
std::cout << "State2::on_entry()" << std::endl;
}
template <class Event,class Fsm>
void on_exit(Event const&, Fsm&) {
std::cout << "State2::on_exit()" << std::endl;
}
};
// Set initial state
typedef State1 initial_state;
// Actions
struct Action {
template <class Event, class Fsm, class SourceState, class TargetState>
void operator()(Event const& e, Fsm& f, SourceState&, TargetState&) const {
// get your_app via Fsm.
f.ya_.do_some_business(e.param);
}
};
// Transition table
struct transition_table:mpl::vector<
// Start Event Next Action Guard
msmf::Row < State1, Event1, State2, Action, msmf::none >,
msmf::Row < State2, Event1, State1, Action, msmf::none >
> {};
your_app& ya_;
};
friend class sm; // give the friend access to the sm
typedef msm::back::state_machine<sm_def> sm;
sm sm_;
};
int main() {
your_app ya;
ya.process_event(your_app::Event1{42});
ya.handle_some(44);
}
И работает демо: https://wandbox.org/permlink/PQGSGr0bnJHgaMpD