2013-10-04 51 views
3

有一次我實現了一個狀態機這樣的:有什麼辦法可以調用模板參數類的未知方法嗎?

class Player 
{ 
public: 
    int Run(); 
    int Jump(); 
    int Stop(); 

private: 
    class State 
    { 
    public: 
     virtual int Run() = 0; 
     virtual int Jump() = 0; 
     virtual int Stop() = 0; 
    }; 

    class StandingState : public State 
    { 
     virtual int Run() { /*...*/ } 
     virtual int Jump() { /*...*/ } 
     virtual int Stop() { /*...*/ } 
    }; 

    class RunningState : public State 
    { 
     virtual int Run() { /*...*/ } 
     virtual int Jump() { /*...*/ } 
     virtual int Stop() { /*...*/ } 
    }; 

    // More states go here! 

    std::list<State*> states; 
    State* currentState; 
}; 

int Player::Run() 
{ 
    int result = m_currentState->Run(); 

    // do something with result 
} 

int Player::Jump() 
{ 
    int result = m_currentState->Jump(); 

    // do something with result 
} 

int Player::Stop() 
{ 
    int result = m_currentState->Stop(); 

    // do something with result 
} 

相當教材我認爲:Player代表從外部調用當前State對象,並做一些事情,結果(可能轉換爲其他狀態)。實質上,每個狀態都知道給定的動作如何影響它,但是狀態機需要將各種狀態連接在一起。我發現這是一個很好的問題分離。

但我看到了這裏的抽象的可能性。整個系統由State類的接口定義:

  1. 兩個狀態機和所述子狀態實施State
  2. 該狀態機保持一個指針,指向所有可能State s,並且當前State
  3. 無論的State方法被稱爲狀態機,它被undiscerningly轉發到當前狀態。

所以,我們完全可以使這個類模板,對不對?看:

template< class StateInterface > 
class StateMachine : public StateInterface 
{ 
    // public methods already declared in StateInterface 

protected: 
    std::list<StateInterface*> states; 
    void AddState(StateInterface* state); 
    StateInterface* currentState; 
}; 

class PlayerStateInterface 
{ 
public: 
    virtual int Run() = 0; 
    virtual int Jump() = 0; 
    virtual int Stop() = 0; 
}; 

class Player : public StateMachine<PlayerStateInterface> 
{ 
public:  
    virtual int Run() { currentState->Run(); /* do stuff */ } 
    virtual int Jump() { currentState->Jump(); /* do stuff */ } 
    virtual int Stop() { currentState->Stop(); /* do stuff */ } 
}; 

以上幾點,這有1和2覆蓋,但3呢?我仍然需要手動將調用委託給具體狀態機實現中的當前狀態。有沒有辦法將該功能移至StateMachine模板?我能以某種方式表達每當StateInterface一個方法被調用StateMachine應該呼籲currentState同樣的方法,當我不知道StateInterface的方法的名稱或簽名?

+0

你可能會有興趣看看[STTCL(http://makulik.github.io/sttcl/) - 我用正是原理設計而成。 (對不起一些缺點:接口方法(事件),需要由頂層和子狀態機是已知的) –

+2

我想'StandingState'和'RunningState'從'State'推導第一碼塊? – dyp

+0

@DyP:Bloops!很對,編輯。謝謝。 – suszterpatt

回答

1

如果你正在尋找一個籠統的回答到RunJumpStop有不同的簽名的情況下,我不知道如果有一個很好的解決方案。然而,在你的榜樣,他們都具有相同的簽名,這表明,我認爲以下方法可能會奏效:

#include <iostream> 

class AbstractState 
{ 
public: 
    virtual void write1() = 0; 
    virtual void write2() = 0; 
}; 

class State1: public AbstractState 
{ 
public: 
    virtual void write1() { std::cout << "1-1" << std::endl; } 
    virtual void write2() { std::cout << "1-2" << std::endl; } 
}; 

class State2: public AbstractState 
{ 
public: 
    virtual void write1() { std::cout << "2-1" << std::endl; } 
    virtual void write2() { std::cout << "2-2" << std::endl; } 
}; 

template <typename StateInterface> 
class Player 
{ 
public: 
    Player(StateInterface *s_): 
     s(s_) 
    { 
    } 

    void setState(StateInterface *s_) 
    { 
     s = s_; 
    } 

    void execute(void (StateInterface::*method)()) 
    { 
     (s->*method)(); 
    } 
private: 
    StateInterface *s; 
}; 

int main() 
{ 
    State1 s1; 
    State2 s2; 

    Player<AbstractState> p(&s1); 

    p.execute(&AbstractState::write1); 
    p.execute(&AbstractState::write2); 

    p.setState(&s2); 

    p.execute(&AbstractState::write1); 
    p.execute(&AbstractState::write2); 

    return 0; 
} 

我能夠編譯和GCC 4.5.2運行這一點,得到了預期的結果,即:

1-1 
1-2 
2-1 
2-2 

正如我說的,我不知道有這個延伸到哪裏AbstractState不同的成員函數採取不同的參數或者返回不同的值的情況下,一個很好的方式,並且可能有其他缺點我還沒有考慮。這並不像我認爲你希望找到的那麼好,但希望這至少可以作爲一個很好的起點。

相關問題