2012-08-24 12 views
6

Sto progettando un pattern Observer che dovrebbe funzionare in questo modo: osservatore chiama AddEventListener metodo EventDispatcher e passa una stringa che è il nome del event, PointerToItself e PointerToItsMemberMethodC++ proprio Observer modello

Successivamente, lo event si verifica all'interno dello EventDispatcher; guarda l'elenco degli abbonamenti e se ce ne sono alcuni, assegnato a questo evento chiama il metodo action dello observer.

Sono arrivato a questo EventDispatcher.h. ATTENZIONE contiene bit di pseudo-codice.

L'sono due domande:

  1. Come faccio a definire il tipo di action in struct Subscription?
  2. Sto spostando nel modo giusto?

PS: No, io non sono gonna utilizzare boost o qualsiasi altre librerie.

#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); 
}; 

Questo collettore implementa simili 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

Per male si' Non usare boost, dal momento che ciò consentirebbe una soluzione facile e sicura del tipo che supererebbe l'approccio attuale e sarebbe più flessibile. Sono consentite le soluzioni C++ 11? – Ylisar

+0

Non lo so ancora, cos'è C++ 11 ... È un nuovo standard, giusto? Mi chiedo se il mio 'g ++ 'lo sa già? Il nuovo standard è ok da usare, non è una libreria ... – Kolyunya

risposta

1

forse si dovrebbe solo creare una classe per essere derivata da "utenti":

class Action { 
    public: 
     friend class EventDispatcher; 

     virtual SomeResultType DoThis() = 0; 

    private: 
     /* Some common data */ 
}; 

semplicemente passare qualche derivata dalla classe-azione digitato variabile addEventListener. Quando viene attivato l'evento corrispondente, è sufficiente compilare i dati comuni e chiamare il metodo DoThis().

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

Per addEventListener:

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

Un esempio di una classe derivata Azione:

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); 

Questo è uno dei modo più sicuro per realizzare azioni (e callback).

+0

Posso passare a 'AddEventListener' un puntatore a una funzione membro di' observer' e chiamarlo da 'EventDispatcher'? Come posso farlo? Grazie? – Kolyunya

+0

Ci scusiamo per il ritardo. Si prega di evitare l'uso di puntatori di funzione. Nel tuo metodo DispatchEvent(), potresti avere solo action-> DoThis(); –

+0

Mi dispiace, ma non capisco ... Diciamo che l'osservatore ha un metodo non statico 'DoSmth()'. Come faccio a passare questo metodo a 'EventDispatcher' e come' EventDispatcher' chiamerà questo metodo in seguito? – Kolyunya

1

Nella sua forma più semplice u_ù potrebbe essere un puntatore a funzione ad esempio

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

quindi si fornisce solo una funzione che viene chiamata ogni volta che viene attivato l'evento.

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

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

È possibile definire una classe Action o passare una funzione lambda (C++ 11).In quest'ultimo caso, l'azione potrebbe essere definita come

function<void (EventDispatcher*)> action; 

e si dovrebbe registrare l'osservatore come segue

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

Probabilmente è meglio usare puntatori deboli intelligenti per memorizzare gli Osservatori nel EventDispatcher, in modo tale che si non devi preoccuparti di annullare la registrazione.

Edit: Aggiunto seguente esempio (solo abbonamento possibile, ma dovrebbe illustrare l'idea - bisogna stare attenti che non si fa riferimento a un oggetto che non esiste più)

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

Posso passare a 'AddEventListener' un puntatore a una funzione membro dell'osservatore e chiamarlo da 'EventDispatcher'? Come posso farlo? Grazie? – Kolyunya

+0

@Kolyunya semplicemente passa 'std :: bind (Observer :: whateverMethod, me)' come funzione al posto del lambda. –

+0

Non ho capito il tuo codice ... Deve essere un nuovo standard C++ 11, non lo so ancora ... Grazie e scusa ... – Kolyunya