Конечные автоматы, инкапсуляция и ООП дизайн

Я внедряю FSM, используя библиотеку Boost's MSM.

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

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

У меня есть объект, который представляет конечный автомат (это boost::msm::back::state_machine<MyStateMachine> где MyStateMachine моя реализация, которая наследует от boost::msm::front::state_machine_def).

Хитрость в том, что моя бизнес-логика будет выполнена в Actions. Я не думаю, что это необычно для FSM. Примеры Boost, кажется, предполагают, что эти Действия должны быть реализованы как методы в самом автомате, но я думаю, что это может сделать один класс слишком массивным. Я чувствую, что имеет смысл отделить работу от государственной машины.

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

  1. Делайте бизнес-логику в методах класса FSM (я беспокоюсь, что это слишком смешивает управление состоянием и работу).

  2. Выполните бизнес-логику в родительском объекте, который создает экземпляр FSM. ФСМ понадобится указатель на родительский элемент, а родительский должен будет реализовать интерфейс, понятный ФСМ (или реализация ФСМ должна будет # включать объявление родителя).

  3. Выполните бизнес-логику в новом классе, который создается и принадлежит FSM.

  4. Выполните бизнес-логику в новом классе, который создается и принадлежит родительскому объекту, но передается в качестве ссылки (или указателя) на FSM.

  5. Что-то другое.

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

Другие вопросы по тегам