用 C++ 设计和实现接口

Designing and implementing an interface in C++: I'm damned if I do and I'm damned if I don't

我需要一些有关 C++ 接口继承和编码方面的帮助。我正在创建一个状态机,我将从中派生出另一个状态机。因此我有两个相互作用的部分,一个 StateMachine 和一个 State。问题是,对于这种设计,我无法从 StateMachineDerivedStateMachine.

派生出专门的 State
class StateMachine; //fwd

class State {
public:
    virtual void action(StateMachine *engine) = 0;

    virtual ~State() = default;
};

class StateMachine {
public:
    StateMachine() = default;

    explicit StateMachine(State &state) : state_(&state) {};

    virtual void setState(State &state) {
        state_ = &state;
    }

    [[nodiscard]] virtual State *getState() const {
        return state_;
    }

protected:
    State *state_ = nullptr;

};
class DerivedStateMachine : public StateMachine {
public:
    using StateMachine::StateMachine;

    int doSpecializedThings(){
        return special_thing_;
    }

    void setSpecialState(int specialState) {
        special_thing_ = specialState;
    }

private:
    int special_thing_;
};

DerivedStateDependsOnGeneralStateMachine 的问题是 StateMachine* 无法访问 doSpecializedThings() 方法。

class DerivedStateDependsOnGeneralStateMachine : public State {

    virtual void action(StateMachine *engine) {
        int important_value = engine->doSpecializedThings();
    };

};

DerivedStateDependsOnDerivedStateMachine 的问题是您不能用不同的参数覆盖父方法。

class DerivedStateDependsOnDerivedStateMachine : public State {

    void action(DerivedStateMachine *engine) override {
        engine->doSpecializedThings();
    };

};

任何人都可以找到摆脱我自己的这个结的出路吗?显然我在某处搞砸了设计。在这种情况下,'normal'(找不到更好的词)是什么?

编辑

为了解决这些评论,首先 @Ted Lyngmo 我正在使用我在 this post 中找到的状态机设计。奇怪的是,相互依赖是设计中内置的,但它是故意的。 StateMachine 需要始终保持对状态的引用,并且状态负责确定下一个状态。下一个状态可能取决于存储在 StateMachine 中的变量,因此在 State.

中引用 StateMachine 很有用

为了更具体一点,我正在编写一个扑克游戏。我的想法是有一个抽象的 StateMachine 接口,我可以从中派生出一个符合该接口的 PokerEngine 状态机。我还认为我应该有一个抽象State,从中我可以推导出一个PokerState。这一切都可以在没有抽象级别的情况下工作,并且最初可能更容易考虑,但对我来说练习接口编码以启用依赖注入和模拟很重要。具有讽刺意味的是,在尝试做这些事情时,我违反了 Liskov 替换原则。无论如何,PokerEngine 将需要做类似 getPotAmount 的事情,这是一种我不想 any StateMachine.

我不完全理解你的设计,但你可以像这样做你需要的:

class StateMachine; //fwd

class State {
public:
    virtual void action(StateMachine *engine) = 0;

    virtual ~State() = default;
};

class StateMachine {
public:
    StateMachine() = default;

    explicit StateMachine(State &state) : state_(&state) {};

    virtual void setState(State &state) {
        state_ = &state;
    }
    virtual void setState(int state) {};

    [[nodiscard]] virtual State *getState() const {
        return state_;
    }

    virtual int doSpecializedThings(){
        std::cout << "Do StateMachine things (or nothing)" << std::endl;
        return 0;
    }
    
protected:
    State *state_ = nullptr;

};

class DerivedStateDependsOnGeneralStateMachine : public State {
public:

    virtual void action(StateMachine *engine) {
        int important_value = engine->doSpecializedThings();
    };

};

class DerivedStateDependsOnDerivedStateMachine : public State {
public:
    void action(StateMachine *engine) override {
        engine->doSpecializedThings();
    };

};

class DerivedStateMachine : public StateMachine {
public:
    using StateMachine::StateMachine;

    int doSpecializedThings(){
        std::cout << "Do DerivedStateMachine things " << std::endl;
        return special_thing_;
    }

    void setState(int specialState) {
        special_thing_ = specialState;
    }

private:
    int special_thing_;
};

class AnotherDerivedStateMachine : public StateMachine {
public:
    using StateMachine::StateMachine;

    int doSpecializedThings(){
        std::cout << "Do AnotherDerivedStateMachine things and" << std::endl;
        std::cout << "\tcalling base class: ";
        StateMachine::doSpecializedThings();
        return special_thing_;
    }

    void setState(int specialState) {
        special_thing_ = specialState;
    }

private:
    int special_thing_;
};

int main()
{
    StateMachine sm;
    DerivedStateMachine dsm;
    AnotherDerivedStateMachine adsm;
    DerivedStateDependsOnGeneralStateMachine dsmg;
    DerivedStateDependsOnDerivedStateMachine dsmd;
    
    sm.setState (dsmg);

    StateMachine * pdsm = &dsm;
    pdsm->setState (dsmd);
    dsm.setState (1);

    dsmg.action (&dsm);
    dsmd.action (&dsm);

    dsmg.action (&sm);
    dsmd.action (&sm);

    dsmd.action (&adsm);
    
    return 0;
}

我不明白你为什么需要 StateMachine 中的 State,因为你正在从 State 调用 StateMachine

最后,我能够通过从抽象类型转换为派生类型来访问我需要的变量和方法