2012-08-24 12 views
6

ich einen Beobachter Muster bin der Gestaltung, die auf diese Weise funktionieren sollte: Beobachter AddEventListener Methode der EventDispatcher aufruft und eine Zeichenfolge, die den Namen des event ist, PointerToItself und ein PointerToItsMemberMethodC++ Eigene Observer Pattern

Danach passiert event innerhalb der EventDispatcher; Es sieht durch die Liste der Abonnements und wenn es einige gibt, die diesem Ereignis zugewiesen sind, ruft die action Methode der observer.

Ich bin zu diesem EventDispatcher.h gekommen. VORSICHT enthält ein Bit Pseudo-Code.

Das sind zwei Fragen:

  1. Wie kann ich die Art von action in struct Subscription definieren?
  2. Bewege ich den richtigen Weg?

PS: Nein, ich werde nicht Gebrauch boost oder keine andere Bibliotheken.

#pragma once 

#include <vector> 
#include <string> 

using namespace std; 

struct Subscription 
{ 
     void*     observer; 
     string     event; 
     /* u_u */    action; 
}; 

class EventDispatcher 
{ 
    private: 
     vector<Subscription> subscriptions; 

    protected: 
     void     DispatchEvent (string event); 

    public: 
     void     AddEventListener (Observer* observer , string event , /* u_u */ action); 
     void     RemoveEventListener (Observer* observer , string event , /* u_u */ action); 
}; 

Dieser Header implementiert wie dies in EventDispatcher.cpp

#include "EventDispatcher.h" 

void EventDispatcher::DispatchEvent (string event) 
{ 
    int key = 0; 
    while (key < this->subscriptions.size()) 
    { 
     Subscription subscription = this->subscriptions[key]; 
     if (subscription.event == event) 
     { 
      subscription.observer->subscription.action; 
     }; 
    }; 
}; 

void EventDispatcher::AddEventListener (Observer* observer , string event , /* */ action) 
{ 
    Subscription subscription = { observer , event , action); 
    this->subscriptions.push_back (subscription); 
}; 

void EventDispatcher::RemoveEventListener (Observer* observer , string event , /* */ action) 
{ 
    int key = 0; 
    while (key < this->subscriptions.size()) 
    { 
     Subscription subscription = this->subscriptions[key]; 
     if (subscription.observer == observer && subscription.event == event && subscription.action == action) 
     { 
      this->subscriptions.erase (this->subscriptions.begin() + key); 
     }; 
    }; 
}; 
+2

Um schlecht Sie‘ Verwenden Sie keinen Boost, da dies eine einfache und typsichere Lösung ermöglicht, die Ihren derzeitigen Ansatz übertrifft und flexibler ist. Sind C++ 11-Lösungen erlaubt? – Ylisar

+0

Ich weiß noch nicht wirklich, was C++ 11 ist ... Es ist ein neuer Standard, oder? Ich frage mich, ob mein 'g ++' es schon weiß? Neuer Standard ist in Ordnung, es ist keine Bibliothek ... – Kolyunya

Antwort

1

Vielleicht sollten Sie nur eine Klasse erstellen, indem Sie „Benutzer“ abgeleitet werden:

class Action { 
    public: 
     friend class EventDispatcher; 

     virtual SomeResultType DoThis() = 0; 

    private: 
     /* Some common data */ 
}; 

Nur einige abgeleitete-von-Klasse-Aktion übergeben Variable AddEventListener eingegeben haben. Wenn das entsprechende Ereignis ausgelöst wird, füllen Sie einfach die allgemeinen Daten aus und rufen Sie die DoThis() -Methode auf.

void EventDispatcher::DispatchEvent (string event) 
{ 
    int key = 0; 
    while (key < this->subscriptions.size()) 
    { 
     Subscription subscription = this->subscriptions[key]; 
     if (subscription.event == event) 
     { 
      subscription->action(); 
     }; 
    }; 
}; 

Für AddEventListener:

void EventDispatcher::AddEventListener (Observer* observer , string event , Action* action) 
{ 
    Subscription subscription = { observer , event , action); 
    this->subscriptions.push_back (subscription); 
}; 

Ein Beispiel für eine Aktion abgeleiteten Klasse:

class myAction: public Action { 
    public: 
     // Implement the DoThis() method 
     void SomeResultType DoThis() { 
      cout << "Hello World!"; 
      return SomeValue; 
     } 
}; 

// To use the action, 
myAction* act = new myAction; 
myEventDispatcher.AddEventListener(someObserver, "HelloWorld", act); 

Dies ist eine der sichersten Weg zur Durchführung von Maßnahmen (und Rückrufe).

+0

Kann ich einfach 'AddEventListener' einen Zeiger auf eine Mitgliedsfunktion des' Beobachters' übergeben und aus dem 'EventDispatcher' aufrufen? Wie kann ich es tun? Vielen Dank? – Kolyunya

+0

Entschuldigung für die Verzögerung. Bitte vermeiden Sie Funktionszeiger. In Ihrer DispatchEvent() -Methode könnten Sie einfach Action-> DoThis(); –

+0

Es tut mir leid, aber ich verstehe es nicht ... Sagen wir, Beobachter hat eine nicht-statische Methode 'DoSmth()'. Wie übergebe ich diese Methode an den 'EventDispatcher' und wie wird' EventDispatcher' diese Methode später aufrufen? – Kolyunya

1

In seiner einfachsten Form u_u zum Beispiel ein Funktionszeiger sein könnte

typedef void (*u_u)(void*); // or whatever arguments u like 

Dann geben Sie nur eine Funktion an, die immer dann aufgerufen wird, wenn das Ereignis ausgelöst wird.

void myaction(void* arg) 
{ 
    ... 
} 

Subscription s; 
... 
s.action = myaction; 
3

Sie könnten entweder eine Action-Klasse definieren oder eine Lambda-Funktion übergeben (C++ 11).Im letzteren Fall könnte Wirkung als

function<void (EventDispatcher*)> action; 

definiert werden und Sie würden die Beobachter registrieren als

Observer * me = this; 
observable->AddEventListener (this, "EventName", [me] (EventDispatcher* dispatcher) { 
    // code here; me is available 
}); 

folgen sollten Sie wahrscheinlich klug schwachen Zeiger verwenden, um die Beobachter in dem Eventdispatcher zu speichern, so dass Sie müssen sich nicht um die Registrierung kümmern.

Edit: Hinzugefügt Beispiel folgende (nur ein Abonnement möglich, aber die Idee veranschaulichen sollte - Sie vorsichtig sein, dass Sie existieren nicht ein Objekt verweisen, die nicht mehr ist)

struct Observable { 
    std::weak_ptr<function<void (const Observable&)>> action; 

    void AddEventListener (std::weak_ptr<function<void (const Observable&)>> theAction) { 
     action = theAction; 
    } 

    void EventRaised() { 
     if (!action.expired()) { 
     auto theAction = action.lock(); 
     (*theAction) (*this); 
     } 
    } 
}; 

struct Observer { 
... 
    void CallOnEvent (const Observable & observable) { 
     // do something 
    } 

    // field to store the action as long as it is needed 
    std::shared_ptr<function<void (const Observable&)>> action; 

    void ... { 
     auto me = this; 
     action = std::make_shared<function<void (const Observable&)>> (
     [me] (const Observable& observable) { 
      me->CallOnEvent (observable); 
     } 
    ); 
     // we could have as well used std::bind 
     observable.AddEventListener (action); 
    } 
}; 
+0

Kann ich einfach '' AddEventListener' 'einen Zeiger auf eine Memberfunktion des Beobachters übergeben und sie aus dem 'EventDispatcher' aufrufen? Wie kann ich es tun? Vielen Dank? – Kolyunya

+0

@Kolyunya pass einfach 'std :: bind (Observer :: whateverMethod, me)' als Funktion anstelle des Lambda. –

+0

Ich bekomme einfach nicht Ihren Code ... Es muss neuer Standard C++ 11 sein, ich weiß es noch nicht ... Danke und Entschuldigung ... – Kolyunya