2012-08-24 17 views
6

Jestem projektowania wzorca obserwatora, który powinien działać w ten sposób: obserwator wzywa AddEventListener metoda EventDispatcher i przekazuje ciąg, który jest nazwą event, PointerToItself i PointerToItsMemberMethodC++ własne Wzorzec Obserwator

Po tym event dzieje się wewnątrz EventDispatcher; przegląda listę subskrypcji i jeśli są jakieś, przypisane do tego zdarzenia wywołuje metodę action z.

Doszedłem do tego EventDispatcher.h. UWAGA zawiera trochę pseudokodu.

są dwa pytania:

  1. Jak określić typ action w struct Subscription?
  2. Czy poruszam się właściwą drogą?

PS: Nie, nie będę Użyj boost lub żadnych innych bibliotek.

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

Nagłówek ten realizuje tak w 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

Aby zły pan” nie używajmy doładowania, ponieważ pozwoliłoby to na łatwe i bezpieczne rozwiązanie, które przewyższałoby obecne podejście i było bardziej elastyczne. Czy dozwolone są rozwiązania C++ 11? – Ylisar

+0

Jeszcze nie wiem, czym jest C++ 11 ... To nowy standard, prawda? Zastanawiam się, czy mój "g ++" już to wie? Nowy standard jest w porządku do użycia, to nie jest biblioteka ... – Kolyunya

Odpowiedz

1

Być może należy po prostu utworzyć klasy mają zostać uzyskane przez „użytkowników”:

class Action { 
    public: 
     friend class EventDispatcher; 

     virtual SomeResultType DoThis() = 0; 

    private: 
     /* Some common data */ 
}; 

Wystarczy przekazać niektóre pochodzą-z-klasy-Action wpisane zmiennej addEventListener. Po wywołaniu odpowiedniego zdarzenia wystarczy wypełnić wspólne dane i wywołać metodę 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(); 
     }; 
    }; 
}; 

Dla addEventListener:

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

Przykładem działań w klasie pochodnej:

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

Jest to jeden z najbezpieczniejszych sposób realizacji działań (i zwrotnych).

+0

Czy mogę po prostu przekazać do 'AddEventListener' wskaźnik do funkcji składowej' obserwatora' i wywołać ją z 'EventDispatcher'? Jak mogę to zrobić? Dziękuję Ci? – Kolyunya

+0

Przepraszamy za opóźnienie. Należy unikać używania wskaźników funkcji. W twojej DispatchEvent() możesz po prostu wykonać akcję-> DoThis(); –

+0

Przykro mi, ale nie rozumiem ... Załóżmy, że obserwator ma niestatyczną metodę 'DoSmth()'. W jaki sposób mogę przekazać tę metodę do 'EventDispatcher' i w jaki sposób' EventDispatcher' wywoła tę metodę później? – Kolyunya

1

W swojej najprostszej postaci może być u_u wskaźnik funkcji np

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

następnie wystarczy podać funkcję, która jest wywoływana, gdy zdarzenie zostanie wyzwolone.

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

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

Można zdefiniować klasę Action lub przekazać funkcję lambda (C++ 11).W tym ostatnim przypadku, działanie może być zdefiniowana jako

function<void (EventDispatcher*)> action; 

i chcesz zarejestrować obserwatora następująco

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

prawdopodobnie powinieneś używać inteligentnych słabe wskaźniki do przechowywania obserwatorów w EventDispatcher tak, że was nie musisz się martwić o rejestrację.

Edycja: Dodano następujący przykład (tylko jeden abonament możliwe, ale powinien zilustrować ten pomysł - trzeba uważać, aby nie odwoływać się do obiektu, który już nie istnieje)

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

Czy mogę po prostu przekazać do 'AddEventListener' wskaźnik do funkcji członka obserwatora i wywołać go z' EventDispatcher'? Jak mogę to zrobić? Dziękuję Ci? – Kolyunya

+0

@Kolyunya po prostu przekazuj 'std :: bind (Observer :: whatMethod, me)' jako funkcję zamiast lambda. –

+0

Po prostu nie dostaję twojego kodu ... To musi być nowy standard C++ 11, jeszcze go nie znam ... Dzięki i przepraszam ... – Kolyunya