He creado dos clases abstractas de sujeto y observador que definen una interfaz de patrón de observador clásica. Derivo de ellos para implementar el patrón Observador. Un observador podría verse así:
void MyClass::Update(Subject *subject)
{
if(subject == myService_)
{
DoSomething();
}
else if(subject == myOtherService_)
{
DoSomethingElse();
}
}
Esto está bien y me dice quién cambió algo. Sin embargo, no me dice qué cambió. A veces esto está bien porque solo voy a consultar el Asunto para obtener los datos más recientes, pero otras veces necesito saber qué cambió exactamente en el Asunto. Noté que en Java tienen un método notifyObservers () y un método notifyObservers (Object arg) para presumiblemente especificar detalles sobre lo que cambió.
En mi caso, necesito saber si ocurrió una de un par de acciones diferentes sobre el tema y, si se trata de una acción en particular, conocer un número entero relacionado con esa acción.
Entonces mis preguntas son:
- ¿Cuál es la forma en C ++ de pasar un argumento genérico (como lo hace Java)?
- ¿Observer es incluso el mejor patrón? ¿Quizás algún tipo de sistema de eventos?
ACTUALIZAR
Encontré este artículo que habla sobre la plantilla del patrón Observador: Implementar un patrón Sujeto / Observador con plantillas . Esto me hizo preguntarme si podrías modelar un argumento.
Encontré esta pregunta de desbordamiento de pila que habla sobre la plantilla del argumento: Patrón de Observador de Sujetos basado en plantillas : ¿Debo usar static_cast o dynamic_cast ? Sin embargo, el OP parece tener un problema que nadie ha respondido.
La otra cosa que podría hacer es cambiar el método de actualización para tomar un objeto EventArg como en:
void MyClass::Update(Subject *subject, EventArg arg)
{
...
Y luego cree subclases de EventArg para datos de argumentos específicos, y luego supongo que lo devuelve a la subclase específica dentro del método de actualización.
ACTUALIZACIÓN 2
También encontré un artículo, Sobre la creación de un marco de trabajo asincrónico basado en c ++; parte 2, que trata de que el Sujeto comunique detalles sobre lo que cambió.
Ahora estoy considerando seriamente usar Boost.Signals . Usar mi propio patrón de observador tenía sentido cuando era simple, pero comenzar a complicar el tipo y el argumento. Y es posible que necesite la seguridad del hilo de Boost.Signals2.
ACTUALIZACIÓN 3
También encontré algunos artículos interesantes sobre el patrón de observación:
Observador generalizador por Herb Sutter
Implementación del patrón de observador en C ++ - Parte 1
Experiencias de implementación del patrón de diseño de observador (Parte 2)
Experiencias de implementación del patrón de diseño de observador (Parte 3)
Sin embargo, he cambiado mi implementación para usar Boost.Signals que, aunque posiblemente un poco hinchado para mis propósitos, funciona con éxito. Y probablemente cualquier preocupación de hinchazón o velocidad sea irrelevante.
fuente
Respuestas:
Ya sea C ++ o JAVA, una Notificación al observador puede venir junto con la información de lo que ha cambiado. Los mismos métodos notifyObservers (Object arg) también se pueden usar en C ++ también.
En general, el problema seguirá siendo que podría haber múltiples sujetos enviados a uno o múltiples observadores y, por lo tanto,
class arg
no se puede codificar.Por lo general, la mejor manera de hacerlo es hacer arg en forma de mensajes / tokens genéricos que formen el mismo tipo de datos para varias clases pero los valores difieren para las diferentes clases observadas. Alternativamente, si todos esos valores de notificación se derivan de la clase en alguna clase basada que es común a todos.
Para el patrón de observador, es importante que el tipo de datos Arg no esté codificado entre el observador y el observador; de lo contrario, es un acoplamiento que dificulta la evolución.
EDITAR
Si desea que el observador no solo observe, sino que también necesite realizar muchas otras tareas en función de lo que ha cambiado en ese momento, también puede consultar el Patrón de visitante . En el patrón de visitante, el observador / visitante llama al objeto modificado y, por lo tanto, no solo puede saber cuál es la modificación, sino que también puede trabajar en él
fuente
Object
(void *
,boost::any
o algo similarmente genérica) que si se pasa tipo específico, ya que con el tipo específico verá en tiempo de compilación que algo ha cambiado, mientras que con el tipo genérico se compilará y dejará de funcionar, porque el observador no podrá trabajar con los datos reales pasados.std::function
Boostboost::function
, Gtk +GClosure
, métodos enlazados de Python, etc.), así que solo defino métodos con las firmas apropiadas y le pido al sistema que cree el archivo real observador. El acoplamiento es de hecho solo una forma, el sujeto define la interfaz para los observadores, pero no tiene idea sobre su implementación.Existen algunas formas de enviar un argumento de evento genérico "Java Like" en C ++.
1) Declare el argumento del evento como nulo * y vuélvalo a la clase correcta en el controlador de eventos.
2) Declare el argumento del evento como puntero / referencia a una nueva clase / interfaz como (en respuesta a su ejemplo)
Y haga que las clases de argumentos de eventos reales se deriven de esta clase.
En cuanto a su pregunta sobre un observador basado en plantilla, consulte
http://www.codeproject.com/Articles/3267/Implementing-a-Subject-Observer-pattern-with-templ
fuente
No sé si este es el nombre canónico, pero de mis viejos días de Smalltalk recuerdo el término "aspecto" para identificar lo que ha cambiado en lo observable.
No es tan complejo como su idea de EventArg (y subclasificarla); simplemente pasa una cadena (incluso podría ser una constante entera) del observable al observador.
Además: solo hay dos métodos simples (
update(observable)
yupdateAspect(observable, aspect)
Menos: el observador puede tener que pedirle al observable más información (es decir, su "número entero")
fuente