2014-11-03 102 views
0

我正在爲2D圖形系統設計一個簡單的事件管理器。
設計看起來像這樣。
要添加事件,您必須定義2個成員函數和一個向量。
但是這些代碼可能會一遍又一遍地重複。什麼是避免重複代碼的最佳設計?

class EventManager 
{ 
private: 
    typedef std::function<void(float,float)> MouseClickFunction; 
public: 
    void registerMouseClickHandler(MouseClickFunction f) 
    { 
     mouseClickHandlers.push_back(f); 
    } 
    void reportMouseClickEvent(float x, float y) 
    { 
     for(auto& f: mouseClickHandlers) 
      f(x,y); 
    } 
private: 
    std::vector<std::function<void(float,float)>> mouseClickHandlers; 
}; 

如何使添加事件的過程更簡單?

(我不是在尋找類似Qt的連接或Boost.Signals,這將是這個太複雜了,在我看來)。

+1

而不是讓你的處理程序接受兩個浮點參數,定義'struct MouseClickEventArgs {float;浮動; }'而是接受。對所有事件處理程序遵循相同的模式。然後,您可以對參數類型進行模板化,並且只有一個EventManager 而不是重複。 – dlf 2014-11-03 18:46:28

+0

一般而言,如果用戶數量不變,則不需要矢量。如果您想要動態更改訂閱者或偵聽器的數量,則需要一個向量。研究「發佈者/訂戶」設計模式。 – 2014-11-03 18:46:33

+0

@dif:代碼仍然重複,您可以更輕鬆地重複代碼。 – 2014-11-03 18:47:31

回答

2

正確使用可變參數模板:

template<class... T> struct EventManager 
{ 
    typedef std::function<void(T...)> notifyFunction; 
    void register(notifyFunction f) 
    { 
     observers.push_back(f); 
    } 
    void notify(T... x) 
    { 
     for(auto& f: observers) 
      f(x...); 
    } 
private: 
    std::vector<notifyFunction> observers; 
}; 

使用一個EventManager<...>爲每個事件。

儘管有一個警告:這個事件管理器依賴於所有的函子,直到它被銷燬,都沒有註銷的規定!

0

Deduplicator提供了一個很好的答案。我在這裏提出一個更簡單的方法。

原則是,你可能希望爲所有種類的事件保留一個獨特的事件管理器。然後,對於不同類型的事件,而不是具有不同類型的參數的不同註冊功能,我建議有一個Event類,我們假設它包含處理事件所需的所有信息。

這裏一般(非常簡化的)想法:

class EventManager { 
private: 
    typedef std::function<void(Event)> EventHandlingFunction; 
public: 
    void registerEventHandler(Event::Type t, EventHandlingFunction f) { 
     EventHandlers[t].push_back(f); 
    } 
    void reportEvent(Event e) { 
     for (auto& f : EventHandlers[e.getType()]) 
      f(e); 
    } 
private: 
    std::map<Event::Type, std::vector<EventHandlingFunction> > EventHandlers; 
}; 

這種方法:

class Event { // simplified class 
public: 
    enum Type { MouseClick, KeyPress, MouseDOubleClick }; // kind of element 
    Event(Type t, float x, float y) : t(t), x(x), y(y) {} // create event with required data 
    Type getType() { return t; } 
    float getX() { return x; } 
    float getY() { return y; } 
private: 
    float x, y; 
    Type t; 
}; 

事件管理器將被打造map圍繞關聯到每個類型的事件處理程序的vector非常簡單。不需要增加註冊功能。它的主要缺點是你可能無意中爲錯誤的元素類型註冊了一個事件處理函數。

注:如果概念證明了這一點符合你的需求,你可以設想,以進一步完善它。例如,您可以使用Event作爲基類,這可以通過事件家族(例如鼠標事件與鍵盤事件)進行專門化。這當然需要審查處理程序簽名,並預見一種事件工廠。