Modello di osservatore; sapendo * cosa * è cambiato?


10

Ho creato due classi astratte: Soggetto e Osservatore che definiscono una classica interfaccia del modello Observer. Derivo da loro per implementare il modello Observer. Un osservatore potrebbe assomigliare a questo:

void MyClass::Update(Subject *subject)
{
    if(subject == myService_)
    {
        DoSomething();
    }
    else if(subject == myOtherService_)
    {
        DoSomethingElse();
    }
}

Questo va bene e mi dice chi ha cambiato qualcosa. Tuttavia, non mi dice cosa è cambiato. A volte questo va bene perché ho intenzione di interrogare l'oggetto per gli ultimi dati, ma altre volte ho bisogno di sapere cosa è cambiato esattamente sull'oggetto. Ho notato in Java che hanno sia un metodo notifyObservers () che un metodo notifyObservers (Object arg) per presumibilmente specificare i dettagli di ciò che è cambiato.

Nel mio caso ho bisogno di sapere se una di una coppia di azioni diverse sono avvenute sull'argomento e, se si tratta di un'azione particolare, di conoscere un numero intero relativo a quell'azione.

Quindi le mie domande sono:

  1. qual è il modo C ++ per passare un argomento generico (come fa Java)?
  2. Observer è anche il modello migliore? Forse una specie di sistema di eventi?

AGGIORNARE

Ho trovato questo articolo che parla del modello del modello Observer: Implementazione di un modello soggetto / osservatore con modelli . Questo mi ha fatto domandare se si potesse modellare un argomento.

Ho trovato questa domanda di overflow dello stack che parla del modello dell'argomento: modello di osservatore soggetto basato su modello - Dovrei usare static_cast o dynamic_cast . Tuttavia, l'OP sembra avere un problema a cui nessuno ha risposto.

L'altra cosa che potrei fare è cambiare il metodo di aggiornamento per prendere un oggetto EventArg come in:

void MyClass::Update(Subject *subject, EventArg arg)
{
  ...

E quindi creare sottoclassi di EventArg per dati di argomenti specifici, quindi immagino di eseguirne il cast nella sottoclasse specifica all'interno del metodo di aggiornamento.

AGGIORNAMENTO 2

Ho anche trovato un articolo sulla creazione di un framework c ++ asincrono basato su messaggi; parte 2 che discute di far comunicare al Soggetto i dettagli di ciò che è cambiato.

Ora sto seriamente pensando di usare Boost.Signals . Usare il mio modello di osservatore aveva senso quando era semplice, ma modellare il tipo e un argomento stava iniziando a complicarsi. E potrei aver bisogno della sicurezza del thread di Boost.Signals2.

AGGIORNAMENTO 3

Ho anche trovato alcuni articoli interessanti sul modello di osservatore:

Generalizing Observer di Herb Sutter

Implementazione del modello di osservatore in C ++ - Parte 1

Esperienze di implementazione del modello di progettazione dell'osservatore (parte 2)

Esperienze di implementazione del modello di progettazione dell'osservatore (parte 3)

Tuttavia, ho passato la mia implementazione all'utilizzo di Boost.Signals che, sebbene possibilmente gonfio per i miei scopi, funziona correttamente. E probabilmente qualsiasi preoccupazione di gonfia o velocità è irrilevante.


Le domande nel corpo non sembrano corrispondere al tuo titolo. Qual è davvero il nocciolo della domanda?
Nicole,

@Renesis: Attualmente sto usando il modello Observer come nell'esempio di codice all'inizio del mio post. Per il codice su cui sto attualmente lavorando, risulta che devo sapere in modo specifico cosa è cambiato in modo da poter reagire di conseguenza. La mia attuale implementazione del modello di osservatore (che è quello standard) non fornisce queste informazioni. La mia domanda è come ottenere informazioni migliori su cosa è cambiato.
Utente

@Renesis: ecco un thread del forum che fa una domanda simile alla mia: gamedev.net/topic/497105-observer-pattern
Utente

Annuncio aggiornamento2: supporto decisamente con Boost.Signals. È molto più conveniente che arrotolarlo da solo. Esiste anche libsigc ++ se si desidera qualcosa di più leggero solo per questo compito (Boost.Signals usa molto codice dal resto di Boost); non è sicuro per i thread.
Jan Hudec,

Per quanto riguarda i problemi di velocità: non so in particolare quanto sia veloce Boost.Signals, ma questo è solo un problema quando hai una grande quantità di eventi che volano in giro ...
Max

Risposte:


4

Che si tratti di C ++ o JAVA, una notifica all'osservatore può venire con le informazioni di ciò che è cambiato. Gli stessi metodi notifyObservers (Object arg) possono essere usati anche in C ++.

Generalmente, il problema rimarrà è che potrebbero esserci più soggetti che inviano a uno o più osservatori e, quindi, class argnon possono essere codificati.

Di solito, il modo migliore per fare è fare arg sotto forma di messaggi / token generici che formano lo stesso tipo di dati per varie classi ma i valori differiscono per le diverse classi osservate. In alternativa, se tutti questi valori di notifica sono derivati ​​dalla classe su una classe basata che è comune a tutti.

Per il modello di osservatore, è importante che il tipo di dati Arg non sia codificato in modo rigido tra l'osservato e l'osservatore, altrimenti è un accoppiamento che rende difficile l'evoluzione delle cose.

MODIFICA
Se desideri che l'osservatore non solo osservi, ma debba anche svolgere molte altre attività in base a ciò che è cambiato , puoi anche controllare il modello di Visitatore . Nel modello visitatore, l'osservatore / visitatore chiama l' oggetto modificato e quindi non solo può sapere quale sia la modifica, ma può effettivamente lavorarci sopra


5
Se l'osservatore interpreta l'argomento, c'è un accoppiamento, non importa quanto si nasconde il tipo. In realtà io direi che è più difficile evolversi se si passa Object( void *, boost::anyo qualcosa di simile generico) che se si passa tipo specifico, perché con il tipo specifico si vedrà al momento della compilazione che qualcosa è cambiato, mentre con il tipo generico che compilerà e smetterà di funzionare, poiché l'osservatore non sarà in grado di lavorare con i dati effettivi passati.
Jan Hudec,

@JanHudec: sono d'accordo, ma ciò significa che hai creato una sottoclasse di osservatori / soggetti una tantum specifica per ogni argomento (cioè, per ciascun caso d'uso)?
Utente

@JanHudec: anche l'accoppiamento è solo a senso unico. Il soggetto non ha idea degli osservatori. Sì, l'osservatore conosce l'argomento ma non è così che funziona il modello di osservatore?
Utente

1
@Utente: Sì, creo un'interfaccia specifica per ciascun soggetto e ciascun osservatore implementa le interfacce dei soggetti che deve osservare. Bene, tutti i linguaggi che utilizzo hanno puntatori di metodo associati nel linguaggio o nel framework (delegati C #, C ++ 11 std::functionBoost boost::function, Gtk + GClosure, metodi associati a Python ecc.), Quindi definisco i metodi con firme appropriate e chiedo al sistema di creare l'effettivo osservatore. L'accoppiamento è davvero solo un modo, il soggetto definisce l'interfaccia per gli osservatori, ma non ha idea della loro implementazione.
Jan Hudec,

0

Esistono alcuni modi per inviare un argomento evento generico "Java Like" in C ++.

1) Dichiarare l'argomento evento come nullo * e inviarlo alla classe giusta nel gestore eventi.

2) Dichiara l'argomento evento come puntatore / riferimento a una nuova classe / interfaccia come (in risposta al tuo esempio)

class GenericEventArgument
{
  virtual bool didAction1Happen() = 0;
  virtual int getActionInteger() = 0;
};

E far sì che le classi di argomenti dell'evento effettivo derivino da questa classe.

Per quanto riguarda la tua domanda riguardante un osservatore basato su template, controlla

http://www.codeproject.com/Articles/3267/Implementing-a-Subject-Observer-pattern-with-templ


-1

Non so se questo sia il nome canonico, ma dai miei vecchi giorni Smalltalk ricordo il termine "aspetto" per identificare cosa è cambiato nell'osservabile.

Non è complesso come la tua idea di EventArg (e la sua sottoclasse); passa solo una stringa (potrebbe anche essere una costante intera) dall'osservabile all'osservatore.

Inoltre: ci sono solo due metodi semplici ( update(observable)eupdateAspect(observable, aspect)

Meno: l'osservatore potrebbe dover chiedere all'osservabile ulteriori informazioni (ad es. Il tuo "numero intero")

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.