一种“通用”的有限状态机实现

9

我经常需要实现一个对象,它能够根据用户命令切换其行为。例如,这可能是表示连接到PC并通过GUI由用户控制的设备的类的情况。更一般地说,设备必须独立运行,具有自己的操作调度。 enter image description here 由于我想要从特定设备类中“提取”此行为以增强代码重用性,因此在这里我提出了使用Qt的模板化有限状态机类。我还在A类中报告了一个示例用法。你们(比我更有经验的程序员)认为呢?这是设计这样一个类的“正确”方式吗?是否存在性能问题?

template < class Base,
           typename T,
           class ThreadPolicy>
class FSM
{
public:
    typedef bool (Base::*my_func)();
    struct SState {
        SState(){}
        SState(const T& id_arg,
               const T& next_arg,
               const T& error_arg,
               const QList<T>& branches_arg,
               const my_func& op_arg) :
            id(id_arg),
            next(next_arg),
            error(error_arg),
            branches(branches_arg),
            op(op_arg)
        {}
        T id;       // state ID
        T next;    // next state
        T error;    // in case of error
        QList<T> branches; // allowed state switching from current
        my_func op; // operation associated with current state
    };
    typedef QMap<T ,SState> SMap;
    bool switchState(const T& ns){
        return _checkAllowed(ns);
    }
    bool addState(const T& id, const SState& s){
        return _register(id, s);
    }
protected:

    void _loop(Base* ptr){
        if ((ptr->*m_states[m_state].op)()) {
            ThreadPolicy::Lock();
            if(m_externalSwitch){
                m_externalSwitch = false;
                ThreadPolicy::Unlock();
                return;
            }
            m_state = m_states[m_state].next;
            ThreadPolicy::Unlock();
        } else {
            ThreadPolicy::Lock();
            if(m_externalSwitch){
                m_externalSwitch = false;
                ThreadPolicy::Unlock();
                return;
            }
            m_state = m_states[m_state].error;
            ThreadPolicy::Unlock();
        }
    }
    bool _checkAllowed(const T& cmd){
        if (!m_states[m_state].branches.contains(cmd)) { return false;}
        ThreadPolicy::Lock();
        m_state = cmd;
        m_externalSwitch = true;
        ThreadPolicy::Unlock();
        return true;
    }

    bool _register(const SState& s){
        if(m_states.find(s.id) != m_states.end()) { return false; } // state with same ID already exist
        m_states[s.id] = s; // add the new state to the map
        return true;
    }
    SMap m_states; // map states to Baseclass methods
    T m_state;  // holds my current state
    bool m_externalSwitch; // check if user request a state switch
};

class A :
        public QObject,
        public FSM< A, QString, MultiThreaded >
{
    Q_OBJECT
    A(){
//        SState startState; myState.branches << "start" << "stop";
        _register(SState("start",
                         "start",
                         "stop",QStringList(("start","stop")),
                         &A::_doStart));
        _register(SState("stop",
                         "stop",
                         "stop",QStringList(("stop","start")),
                         &A::_doStop));
    }

private slots:
    void run(){
        for(;;){
            _loop(this);
            QCoreApplication::processEvents();
        }
    }
private:
    bool _doStart(){ return true;}
    bool _doStop(){ return true;}

};

1
顺便说一句,你所问的问题更适合于 Stack Overflow 的代码审查网站,如果你只是想知道你的代码是否好:http://codereview.stackexchange.com - πάντα ῥεῖ
2个回答

8

A. 你们比我更有经验的程序员们,你们认为这个设计方式怎么样?这是“正确”的设计方式吗?是否存在性能问题?

好的!我大致查看了您的设计,对于通用的FSM框架来说,它并不是很好。它过于狭窄,不能在扩展的上下文中使用。以下是一些批评点:

  1. 您依赖于Qt :(; 至少应该使用C++ STL组件进行实现。
  2. 您的状态应该是(专门的)类本身,直接实现行为。FSM类本身,应该独立于它们的行为(尤其不要实现)。
  3. 您不支持更复杂的状态图,包括子状态(机器)/复合状态、并发FSM路径(分叉、交汇)、活动状态(重复异步操作)等。
一般来说,我建议使用GoF State Pattern来实现FSM。对于非常简单的状态图,switch(event)case <event>: changeState(newState)可能足够了。但是将事件呈现为FSM方法条目,并将其委托给当前状态类实例,可以使整个结构更加灵活。考虑到特定事件随附的可选参数,您需要为这些参数扩展状态机设计。
一般而言,使用CRTP作为状态机的方法是一个好主意,但就您所展示的内容而言,简单的动态多态性(使用虚成员函数)也可以很好地工作。
关于性能问题,不要认为您的当前环境会遇到问题,但完全取决于您要部署的位置和上下文。

我想向您推荐一下我的状态机类模板框架STTCL,它提供了符合UML 2.0标准的各种基于C++模板的状态机方面,遵循已经提到的GoF状态模式。


1
如果这还有参考价值,我已经在C++中实现了一个有限状态机,它使用了对象OP,非常简单易用。如果您查看main.cpp文件,那里有一个示例。

代码在这里,并且现在已编译为库。

有限状态机

如果这是您想要的,请告诉我!

祝好,

Andrea


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接