2012-08-24 13 views
6

Estoy diseñando un patrón de observador que debe trabajar de esta manera: observador llama AddEventListener método de EventDispatcher y pasa una cadena que es el nombre de la event, PointerToItself y una PointerToItsMemberMethodC++ propio patrón Observador

Después de eso event sucede dentro del EventDispatcher; mira a través de la lista de suscripciones y, si hay algunas, asignadas a este evento llama al método action del observer.

He venido a este EventDispatcher.h. PRECAUCIÓN contiene un poco de pseudocódigo.

El son dos preguntas:

  1. ¿Cómo definir el tipo de action en struct Subscription?
  2. ¿Me estoy moviendo de la manera correcta?

PS: No, no voy uso boost o cualquier otras bibliotecas.

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

Esta cabecera implementa como este en 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

A malo que' No estoy usando boost, ya que eso permitiría una solución segura fácil y de tipo que superaría su enfoque actual y sería más flexible. ¿Se permiten las soluciones C++ 11? – Ylisar

+0

Realmente no lo sé aún, ¿qué es C++ 11? Es un nuevo estándar, ¿verdad? Me pregunto si mi 'g ++' ya lo sabe. El nuevo estándar está bien para usar, no es una biblioteca ... – Kolyunya

Respuesta

1

Tal vez eso deberá crear una clase que se deriva de "usuarios":

class Action { 
    public: 
     friend class EventDispatcher; 

     virtual SomeResultType DoThis() = 0; 

    private: 
     /* Some common data */ 
}; 

sólo tiene que pasar algún derivado de la clase-acción mecanografiadas variable para AddEventListener. Cuando se desencadena el evento correspondiente, simplemente complete los datos comunes y llame al método 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(); 
     }; 
    }; 
}; 

Para AddEventListener:

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

Un ejemplo de una clase derivada de Acción:

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

Esta es una de la forma más segura para implementar acciones (y devoluciones de llamada).

+0

¿Puedo pasar a 'AddEventListener' un puntero a una función miembro del' observer' y llamarlo desde 'EventDispatcher'? ¿Cómo puedo hacerlo? ¿Gracias? – Kolyunya

+0

Disculpe la demora. Por favor, evite el uso de indicadores de función. En su método DispatchEvent(), podría simplemente tener acción-> DoThis(); –

+0

Lo siento, pero no entiendo ... Digamos que el observador tiene un método no estático 'DoSmth()'. ¿Cómo paso este método al 'EventDispatcher' y cómo' EventDispatcher' llamará a este método más tarde? – Kolyunya

1

En su forma más simple u_u podría ser un puntero de función, por ejemplo,

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

entonces usted acaba de suministrar una función que se llama cada vez que se desencadena el evento.

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

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

Puede definir una clase de acción o pasar una función lambda (C++ 11).En este último caso, la acción se podría definir como

function<void (EventDispatcher*)> action; 

y se registraría el observador de la siguiente manera

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

Probablemente debería utilizar punteros débiles inteligentes para almacenar los observadores en el EventDispatcher, de modo que se no tiene que preocuparse por cancelar el registro.

Editar: Añadido siguiente ejemplo (sólo una suscripción posible, sino que debe ilustrar la idea - usted tiene que tener cuidado de que no se hace referencia a un objeto que ya no existe)

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

¿Puedo pasar a 'AddEventListener' un puntero a una función miembro del observador y llamarlo desde' EventDispatcher'? ¿Cómo puedo hacerlo? ¿Gracias? – Kolyunya

+0

@Kolyunya acaba de pasar 'std :: bind (Observer :: whateverMethod, me)' como función en lugar de la lambda. –

+0

Simplemente no consigo su código ... Debe ser un nuevo estándar C++ 11, aún no lo sé ... Gracias y lo siento ... – Kolyunya

Cuestiones relacionadas