【问题标题】:State Machine Designing状态机设计
【发布时间】:2014-10-20 08:08:45
【问题描述】:

我正在使用 c++ 中的协议设计代码,需要有关状态机设计的帮助。

我们拥有的所有状态机都具有相同的性质。每个状态机都有特定的状态(S1、S2 等),每个状态只能接受特定类型的事件(E1、E2 等)。根据事件处理的结果,机器进入下一个状态。

例如,从状态 S1 和事件 E1,机器可以移动到 S2 或 S3。我研究了状态设计模式,但它建议所有状态(派生状态类)都应该实现状态机可以执行的所有动作,或者基状态类应该实现这些动作。在我的情况下,单个状态只能处理某些事件,因此我认为状态设计模式不适用。

您能否建议实施此类机器的最佳方式/模式。

【问题讨论】:

  • 那么当您处于状态S1 并且您收到未处理的事件E62 时会发生什么?
  • 这个问题似乎离题了,因为它是关于软件设计的,这在programmers.stackexchange.com上会更好
  • 你看过Boost Statechart吗?不确定它是否正是您需要/想要的,但它可能有用。
  • @sharth 如果我在状态 S1 中收到一个事件 E62,而 S1 并不意味着处理 E62,那么这意味着该事件已被错误地生成,我们将丢弃该事件并简单地记录一个错误。跨度>
  • 状态机没什么大不了的。如果它们很少更改,您可以利用每个状态机对应一个正则表达式,每个正则表达式对应一个简单的结构化程序(带有 if 和 while)这一事实。

标签: c++ design-patterns state-machine


【解决方案1】:

根据“四人组”,State 设计模式适用于对象的行为取决于其状态并且它必须在运行时根据该状态改变行为。

您的状态机的描述(根据事件更改状态)似乎与此描述相符。

class StateMachine {    // state machine that processes events received
    State *s; 
public:  
    StateMachine();         // constructor: here you shall set initial state
    void process (Event* e); // processes the event and sets the next state
    bool isReady ();        // example of other requests
};

这种模式的原则是有一个抽象基类,定义了所有可能的状态相关的“动作”,StateMachine 可以委托给一个状态。

class State {
public: 
      virtual void process (StateMachine *m, Event* e)=0;    // called by state machine /  pure virtual MUST be implemented in concrete state 
      virtual bool isReady(StateMachine *m);  // example  
      ...                               // other "actions"     
};

您的状态和状态机之间的接口将很容易定义。例如:

void StateMachine::Process (Event *e) {
     s->Process (this, e);   // calls the Process() of the current state 
}

然后,派生的具体状态应实现不同的行为。由于此*模式与 C++ 中的虚函数一起使用,因此必须为每个具体状态(在基类或派生类中)定义所有动作。这是定义虚函数的直接结果。

但是对于与所有状态不相关的操作,您可能有一个默认操作,要么什么都不做,要么触发错误(错误消息、异常……)。这取决于您的总体设计,是否防止这种情况发生。

例如:

bool State::isReady(StateMachine *m) {  // if this request is only relevant for a couple of states
   throw std::exception ("unauthorized request");   // trigger an exception if it happens
   return false;
}    // this is done by default, except for concrete state that defines somtehing else 

根据您的描述,我真的会选择状态模式。

【讨论】:

  • Christophe,感谢您的回答和详细说明。在这种情况下,我可以做的是在基类中定义所有操作,而基类中的默认定义将仅抛出/记录错误。具体的类将只覆盖对它们有意义的操作并保留其他类。
  • 是的,完全正确!你的“行动”会与事件 1:1 对应吗?还是事件对象本身?
  • 动作确实与事件 1:1 对应,同时事件也是对象。从你的回复中,我又想到了一件事。我是否可以从一个只有一个函数的基类派生多个状态( virtual void processEvent ( Event* e ) = 0 )。每个具体状态都会覆盖此函数并实现特定于应用程序的行为。然后我可以创建一个包含 3 个条目(cur_state、event、new_state)的表。通过这种方式,我可以摆脱多个 switch-case 语句,也可以避免在基类中定义多个动作。请求您的 cmets。
  • 绝对!如果您有非常动态的行为并且经常将新事件添加到代码中,那么最好使用流程函数来执行特定操作并决定下一阶段。但如果事件足够稳定,您可以定义 1:1 的成员函数,那么您可以选择状态转换表。我不会把它放在基类中(因为基类不需要知道它的实现),而是放在状态机本身。
  • 感谢克里斯托夫的所有努力。感谢你的帮助。赞成并将您的答案标记为已接受。再次感谢。
猜你喜欢
  • 2011-01-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-12-19
  • 2011-01-07
相关资源
最近更新 更多