2016-04-02 4 views
0

ich nicht sicher mehrere Überlastungen für dervied Klassen zu handhaben war, wie der Titel dieser Frage zu Wort, aber hier ist mein Problem:Wie, ohne dass mehrere Änderungen an anderen Dateien

ich auf einem Spiel in C arbeitete ++ verwenden die Architektur der Entitätskomponentensystem. In dieser Architektur wird jedes Spiel Objekt als Klasse dargestellt:

class Object 

Eine Klasse nicht viel mehr als ein Behälter für Komponenten ist, die Teile des Spiels Objekt Logik behandeln.

class Component 

Jede Komponente verfügt über eine Aktualisierungsfunktion und Ereignishandler für bestimmte Ereignisse. Jedes Ereignis wird von einer gemeinsamen Basisklasse abgeleitet, und jedes untergeordnete Objekt verfügt über eigene Datenfelder zur Beschreibung des Ereignisses. In meinem tatsächlichen Code hat die Basis-Event-Klasse einige Attribute, die alle Events teilen.

class Event 
{ 
}; 

class OnDeathEvent : public Event 
{ 
public: 
    int getKillerID() const 
    { 
     return 92341; 
    } 
}; 

class OnLevelEnterEvent : public Event 
{ 
public: 
    int getLevelEnteredID() const 
    { 
     return 100; 
    } 
}; 

Im gesamten Code senden wir Ereignisse an Objekte. Hier ist die Hauptfunktion schrieb ich mein Beispiel-Code zu testen:

int main() 
{ 
    Object obj; 
    obj.sendEvent(OnLevelEnterEvent()); 
    obj.sendEvent(OnDeathEvent()); 
    std::cin.ignore(); 
    return 0; 
} 

Wenn ein Objekt ein Ereignis empfängt, Verantwortung Delegierten für diesen Event seine Komponenten.

for (auto& component : components) 
    { 
     component->handleEvent(e); 
    } 

Ich möchte Funktion Überladung für jeden Ereignistyp verwenden. Ereignisbehandlungsfunktionen werden dann sehr klar in ihrem Zweck.

Dazu müssen jedoch die Object-Klasse UND die Component-Basisklasse alle Überladungen für jeden Ereignistyp deklarieren. Offensichtlich wollen wir das vermeiden, weil wir mehrere Klassen ändern müssen, um unserem Spiel einen neuen Ereignistyp hinzuzufügen. Hier ist der Rest des Beispielcode:

class Component 
{ 
public: 

    virtual void handleEvent(const Event& e) {} 
    virtual void handleEvent(const OnDeathEvent& e) {}; 
    virtual void handleEvent(const OnLevelEnterEvent& e) {}; 
}; 

class TestComponent : public Component 
{ 
public: 
    virtual void handleEvent(const OnDeathEvent& e) 
    { 
     std::cout << "You died. Your killer was: " << e.getKillerID() << std::endl; 
    } 
}; 

class AnotherComponent : public Component 
{ 
public: 
    virtual void handleEvent(const OnLevelEnterEvent& e) 
    { 
     std::cout << "Level Entered with ID: " << e.getLevelEnteredID() << std::endl; 
    } 
}; 

Die Objektklasse:

class Object 
{ 
public: 
    Object() 
    { 
     components.push_back(new TestComponent()); 
     components.push_back(new AnotherComponent()); 
    } 


    void sendEvent(const Event& e) 
    { 
     for (auto& component : components) 
     { 
      component->handleEvent(e); 
     } 
    } 

    void sendEvent(const OnDeathEvent& e) 
    { 
     for (auto& component : components) 
     { 
      component->handleEvent(e); 
     } 
    } 

    void sendEvent(const OnLevelEnterEvent& e) 
    { 
     for (auto& component : components) 
     { 
      component->handleEvent(e); 
     } 
    } 

private: 
    std::vector<Component*> components; 
}; 

Wir wollen einen neuen Event-Typen hinzufügen können, indem sie von der Event-Klasse erben, ohne Änderungen an andere zu erfordern Dateien oder Erstellen neuer Dateien (außer der neuen Event-Klasse). Was wäre eine elegante Art, unseren Code zu strukturieren?

Alle Vorschläge sind willkommen.

Antwort

0

Wenn ich richtig verstehe Sie wollen eine einzige sendEvent Methode zu, nun, senden Sie alle generischen Event Typ. In diesem Fall können Sie eine sendEvent(const Event* _event) Methode wie folgt erstellen:

void sendEvent(const Event* _event) { 
    for (auto& component : components) { 
     component->handleEvent(_event); 
    } 
} 

Wenn Sie alle abgeleiteten Klassen von Event ordnungsgemäß umgesetzt haben, dann nehmen Sie Polymorphismus kümmert sich um Dinge die richtige handleEvent Methode ermöglicht automatisch von der spezifischen Unter abhängig aufgerufen werden -Klasse von Event (Nun, der Zeiger darauf), die an sendEvent übergeben wurde.

+0

Wenn ich etwas nicht vermisse, würde das nicht funktionieren. Irgendwann würde ich ablegen müssen, da nicht alle Event-Typen die gleiche Event-Schnittstelle teilen. Und Casting ist nicht viel besser als der Code, den ich bereits habe – kgwong

1

Ihre Komponente könnte sich für das Ereignis registrieren. Wenn das untergeordnete Ereignis aufgerufen wird, könnte es seine registrierten Ereignislistener benachrichtigen. Sie könnten eine Schnittstelle definieren und die Vererbung von Schnittstellen verwenden.

+0

Ich bekomme eine ziemlich gute Idee von dem, was Sie sagen, aber ich bin mir nicht ganz sicher, wie das Code-weise aussehen würde. Ein kurzes Beispiel wäre sehr hilfreich. Danke für die Antwort! – kgwong

+0

Funktioniert besser mit Boost-Signalen. – dgsomerton

1
#include <iostream> 
#include <vector> 
#include <boost/signals2.hpp> 
class Event 
{ 
public: 
    typedef boost::signals2::signal<void (Event&) > T_notify_signal; 
    virtual ~Event(){} 
    void Register(const T_notify_signal::slot_type& c) {m_listeners.connect(c);} 
    void NotifyListeners() {m_listeners(*this);} 
    virtual std::string info() const = 0; 
private: 
    T_notify_signal m_listeners; 
}; 
class OnDeathEvent : public Event 
{ 
public: 
    virtual ~OnDeathEvent(){} 
    virtual std::string info() const { return "you are dead"; } 
}; 
class OnLevelEnterEvent : public Event 
{ 
public: 
    virtual ~OnLevelEnterEvent(){} 
    virtual std::string info() const { return "you are in a new area"; } 
}; 
//---------------------------------------- 
class Component 
{ 
public: 
    virtual ~Component(){} 
}; 
class TestComponent : public Component 
{ 
public: 
    TestComponent(){} 
    virtual ~TestComponent(){} 
    virtual void notifyLevelEnter(Event& e) {std::cout << "TestComponent::notifyLevelEnter(" << e.info() << ")" << std::endl;} 
}; 
class AnotherComponent : public Component 
{ 
public: 
    AnotherComponent(){} 
    virtual ~AnotherComponent(){} 
    virtual void notifyDeath(Event& e) {std::cout << "AnotherComponent::notifyDeath(" << e.info() << ")" << std::endl;} 
    virtual void notifyLevelEnter(Event& e) {std::cout << "AnotherComponent::notifyLevelEnter(" << e.info() << ")" << std::endl;} 
}; 
//---------------------------------------- 
class Object 
{ 
public: 
    Object(){} 
    ~Object(){} 
    void addComponent(Component& component) {m_components.push_back(&component);} 
    void sendEvent(Event& e) {e.NotifyListeners();} 
private: 
    std::vector< Component* > m_components; 
}; 
//---------------------------------------- 
int main() 
{ 
    Object obj; 

    // create components 
    TestComponent tc; 
    AnotherComponent ac; 

    // create events 
    OnDeathEvent  death_event; 
    OnLevelEnterEvent level_enter_event; 

    // hook up events to components 
    death_event.Register(boost::bind(&AnotherComponent::notifyDeath, &ac, _1)); 

    level_enter_event.Register(boost::bind(&TestComponent::notifyLevelEnter, &tc, _1)); 
    level_enter_event.Register(boost::bind(&AnotherComponent::notifyLevelEnter, &ac, _1)); 

    // add components to object 
    obj.addComponent(ac); 
    obj.addComponent(tc); 

    // process game loop 
    while (true) 
    { 
     std::cout << "obj.sendEvent(level_enter)" << std::endl; 
     obj.sendEvent(level_enter_event); 

     std::cout << "obj.sendEvent(death)" << std::endl; 
     obj.sendEvent(death_event); 

     std::cout << "obj.sendEvent(level_enter)" << std::endl; 
     obj.sendEvent(level_enter_event); 

     std::cout << "obj.sendEvent(level_enter)" << std::endl; 
     obj.sendEvent(level_enter_event); 

     std::cout << "obj.sendEvent(death)" << std::endl; 
     obj.sendEvent(death_event); 
     break; 
    } 

    return 0; 
} 
Verwandte Themen