状态机改变状态

State Machine Change State

我一直 运行 遇到同样的问题,即使浏览教程也无法解决。 我已经“设置”了我的状态机,但我无法在状态之间转换。

这是我的状态机:

class StateMachine
{
    State* m_State;

public:
    StateMachine();
    ~StateMachine();
    void changeState(State* state);
};

这是一个示例状态:

class A : State
{
public:
    A();
    ~A();
    void handleInput(int a);
}

如果我将 a = 1 传递给 A::handleInput() 我想转换到状态 B。但是当我实现它时我无法从 A::handleInput() 访问 StateMachine,这让我痛苦地擦洗我的头。

But when I implement it I can't access the StateMachine from A::handleInput()

嗯,这是 State Pattern 的一个众所周知的问题,没有提及如何使用封闭的 状态机 [=50] 来保持状态 classes =].

IMO,这是将 StateMachine class 作为 Singleton.
实施的有效用例之一 这样它的实例就可以从任何 Stateclass 实现中访问。

正如我在这里所说的设计模式,State classes 可以在 Flyweight Pattern 的帮助下设计,因为它们他们通常是无国籍人。

我曾经将所有这些驱动到一个 c++ 模板框架中,它抽象了 StateState Machine 的接口(参见 link 下面)。

下面是一个简短的代码示例:

StateMachine.h

struct State {
    virtual void handleInput(int x) = 0;
    virtual ~State() {} = 0;
};

class StateMachine {
    State* m_State;

    StateMachine();
public:
   
    static StateMachine& instance() {
        static StateMachine theInstance;
        return theInstance;
    }
    void changeState(State* state) {
        m_State = state;
    }
    void triggerInput(int x) {
        m_State->handleInput(x);
    }
};

StateA.h

#include "StateMachine.h"
class StateB;
extern StateB* stateB;

class StateA : public State {
public:
    virtual ~StateA() {}
    virtual void handleInput(int x) {
        if(x == 1) {
             // Change to StateB
             StateMachine::instance.changeState(stateB);
        }
        else {
            // Do something with x
        }
    }
};

这里省略了od StateB的定义,应该和StateA一样。


参考文献:

我看过 Sourcemaking 示例,对我来说,实施示例真的很糟糕;必须在每次状态更改时创建新实例: https://sourcemaking.com/design_patterns/state/cpp/1

就我个人而言,作为使用 JK 触发器设计电子设备状态机的人,我会使用类似但语义不同的方法。状态机的复杂性涉及根据状态和输入执行的 action;通常在 C 中,您会使用大量 switch 语句和可能描述如何处理 current statenew input 又名 event.

的数组来执行此操作

所以对我来说,面向对象的方法是对 event handler 进行建模。这将有一个描述输入格式的接口。然后,对于每个不同的状态,您都有该接口的不同实现。这样,状态机就可以简单地将状态集合实现到事件处理程序——数组、向量或映射。尽管处理程序仍然可能包含 case 语句,但整体的意大利面条式已经大大减少了。您可以在必要时使用新的状态处理程序轻松扩展设计:

所以你可以有这样的东西:


#include <map>

typedef enum
{
    //TODO : state list, e.g.
    eOff,
    eOn
}
teCurrentState;

typedef struct
{
    //TODO : Add inputs here, e.g.
    bool switch1;
}
tsInputDesc;

typedef struct
{
    //TODO : Add outputs here, e.g.
    bool relay1;
}
tsOutputDesc;

// ------------------------------------------------

class IEventHandler
{
public:
    virtual ~IEventHandler() {}
    // returns new state
    virtual teCurrentState handleInput(tsInputDesc const& input, tsOutputDesc& output) = 0;
};

// ------------------------------------------------

class OnStateHandler : public IEventHandler
{
public:
    virtual teCurrentState handleInput(tsInputDesc const& input, tsOutputDesc& output) override
    {
        //TODO : IMPLEMENT
        teCurrentState newState = TODO....
        return (newState);
    }
};

// ------------------------------------------------

class OffStateHandler : public IEventHandler
{
public:
    virtual teCurrentState handleInput(tsInputDesc const& input, tsOutputDesc& output) override
    {
        //TODO : IMPLEMENT
        teCurrentState newState = TODO....
        return (newState);
    }
};

// ------------------------------------------------

class StateMachine
{
protected:
    teCurrentState mCurrentState;
    std::map<teCurrentState, IEventHandler*> mStateHandlers;

    void makeHandlers()
    {
        mStateHandlers[eOff] = new OffStateHandler();
        mStateHandlers[eOn] = new OnStateHandler();
    }

public:
    StateMachine()
    {
        makeHandlers();
        mCurrentState = eOff;
    }

    void handleInput(tsInputDesc const& input, tsOutputDesc output)
    {
        teCurrentState newState = mStateHandlers[mCurrentState]->handleInput(input, output);
        mCurrentState = newState;
    }
};

// ------------------------------------------------

void runFsm()
{
    StateMachine fsm;
    tsInputDesc input;
    tsOutputDesc output;
    bool alive = true;

    while (alive)
    {
        // TODO : set input according to....inputs (e.g. read I/O port etc)

        fsm.handleInput(input, output);

        // TODO : use output
    }
}