我经常需要实现一个能够根据用户命令切换其行为的对象。例如,这可能是连接到PC并由用户通过GUI控制的类代表设备的情况。更一般地说,设备必须独立存在,具有自己的操作调度。 由于我想从特定的设备类中“提取”这种行为以增强代码重用,这里我提出了一个使用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;}
};
答案 0 :(得分:8)
一个。你是什么(比我更有经验的程序员:)想一想 那?这是设计这样一个班级的“正确”方式吗?在那儿 性能问题?
OK!我粗略地看了一下你的设计,对于我来说,对于通用的FSM框架我并没有真正感觉良好。这太狭隘了,无法在更广泛的背景下使用。一些批评点:
一般情况下,我建议您按照GoF State Pattern进行FSM实施。对于非常简单的状态图,switch(event)
case <event>: changeState(newState)
可能就足够了。但是要将事件呈现为FSM的方法条目,并将这些事件委托给当前的状态类实例,使整个构造更加灵活。考虑特定事件随附的可选参数,您需要扩展状态机设计。
一般来说,使用CRTP作为状态机的方法是一个好主意,但是对于您所演示的内容,简单的动态多态(使用虚拟成员函数)也可以正常工作。
关于性能问题,不要认为您会使用当前环境获得性能问题,但这完全取决于您要部署的位置和位置。
我想建议您查看我的状态机类模板框架STTCL,该框架提供符合UML 2.0的状态机的各种基于C ++模板的方面,遵循已经提到的GoF状态模式。
答案 1 :(得分:1)
如果它仍然相关,我已经在C ++中实现了一个使用Object OP的有限状态机,它使用起来相当简单,如果你看一下main.cpp就有一个例子。
代码在这里,它现在被编译为库。
让我知道这是否是你想要的!
干杯,
安德烈