Design Patterns - Una guida rapida al modello Observer.

Il modello di osservatore è un modello molto comunemente usato. In effetti, è così comune che viene standardizzato in molti linguaggi / librerie di programmazione. In Java, esiste injava.util.Observer (obsoleto in Java 9). In Python è vicino quanto apip installa pattern-observer. In C ++, a volte possiamo usare la libreria boost, più precisamente #include . Tuttavia, è ampiamente utilizzato nell'industria come soluzione su misura. Per poterlo usare correttamente e comprenderne la complessità, dobbiamo immergerci ed esplorarlo.

Il modello di osservatore è classificato tra i modelli di progettazione comportamentale. I modelli di progettazione comportamentale riguardano in particolare la comunicazione tra classi / oggetti. [di Design Patterns spiegato semplicemente]

Cos'è un modello di osservatore? Oltre a un monitor portatile che trasmette la televisione analogica (come nella foto). L'obiettivo del modello è definire una relazione uno-a-molti in modo tale che quando un oggetto cambia stato, gli altri vengono notificati e aggiornati automaticamente. Più precisamente, desidera essere informato sugli eventi che si verificano nel sistema. Mettiamo insieme i pezzi del puzzle in tre passaggi.

Passaggio 1: parole chiave

La definizione delle parole chiave è la ricetta segreta di questa serie di guide rapide. Questo metodo mi ha aiutato a comprendere veramente i modelli di progettazione, a codificarli nella mia mente e a comprendere le differenze tra gli altri modelli di progettazione.

  1. Oggetto: è considerato il detentore delle informazioni, dei dati o della logica aziendale.
  2. Registra / Allega: gli osservatori si registrano sull'argomento perché vogliono essere avvisati quando c'è un cambiamento.
  3. Evento: gli eventi agiscono come un fattore scatenante nell'argomento in modo tale che tutti gli osservatori siano informati.
  4. Notifica: a seconda dell'implementazione, il soggetto può "inviare" informazioni agli osservatori, oppure gli osservatori possono "tirare" se hanno bisogno di informazioni dal soggetto.
  5. Aggiornamento: gli osservatori aggiornano il loro stato indipendentemente dagli altri osservatori, tuttavia il loro stato potrebbe cambiare in base all'evento attivato.

Passaggio 2: diagramma

Dividiamo questo design in diverse classi per semplificarlo un po '.

  • I ConcreteObservers sono classi che contengono informazioni specifiche sull'istanza corrente. La funzione di aggiornamento viene chiamata dall'operazione di notifica () del soggetto. Gli osservatori si aggiornano indipendentemente in base al loro stato attuale.
  • L'Osservatore è la classe genitrice degli osservatori concreti. Contiene un'istanza soggetto. Quando un osservatore viene inizializzato, si registra / si attacca al soggetto.
  • La classe Subject ha un elenco o una raccolta di osservatori. Quando viene attivato un evento, chiama l'operazione di notifica () che scorre attraverso tutti gli osservatori chiamando la loro funzione di aggiornamento.

Passaggio 3: codice per esempio

Suggerirei di copiare la classe di codice per classe dal mio repository git “Andreas Poyias” o i frammenti di seguito (nell'ordine fornito) e incollarlo in uno qualsiasi degli editor C ++ disponibili online come c ++ shell, jdoodle, onlineGDB ed eseguire per osservare l'output. Quindi leggi i commenti o la descrizione di seguito. Prenditi il ​​tuo tempo, leggendolo attentamente (ciò significa un minuto, non meno e non di più).

Esempio: considera una partita di calcio. Molti tifosi stanno guardando la partita. Abbiamo diviso i sostenitori in due categorie per età, giovani e vecchi. Quando la loro squadra segna un goal, i tifosi reagiscono in modo diverso a seconda della loro età e del loro livello di eccitazione.
Parliamo ora dei termini utilizzati per il modello di osservatore:

  • Il gioco è soggetto e i sostenitori sono gli osservatori.
  • Tutti gli osservatori sono attaccati / registrati sull'argomento e sono avvisati quando la loro squadra di calcio segna (l'evento scatenante è se la loro squadra segna).
  • Gli osservatori aggiornano il loro comportamento in base alla notifica ricevuta.

Soggetto
Per questa classe, abbiamo bisogno di accedere a un elenco di osservatori. Quando gli osservatori stanno per registrarsi, chiamano la funzione theattach (questa) per aggiungersi all'elenco disponibile (questa è l'istanza di un osservatore). Quando un evento viene attivato, indica () tutti gli osservatori per aggiornare in modo indipendente il loro stato. In questo esempio, il grilletto è se la squadra di calcio dell'osservatore ha segnato.

#include 
#include 
usando lo spazio dei nomi std;
soggetto della classe {
    vettore  osservatori;
    bool segnato; // evento scatenante
pubblico:
    // registra gli osservatori
    void attach (Observer * obs) {
        observers.push_back (obs);
    }
   
   // Questo è l'EVENTO
   // imposta se segnato e avvisa TUTTI gli osservatori
   void setScored (bool Score) {
      segnato = Punteggio;
      notificare();
   }
bool getScored () {
      punteggio di ritorno;
   }
   // notifica l'implementazione è più in basso
   // in modo che lo script venga compilato ed eseguito
   void notification ();
};

Osservatore
Questa classe dipende dall'argomento con cui è registrata. Quando gli osservatori concreti vengono inizializzati, si attaccano all'oggetto. In questo esempio, lo stato di ciascun osservatore è la sua eccitazione. Sviluppa il gioco.

osservatore di classe
{
    Oggetto * subj;
    int excitementLevel; // state
  pubblico:
    Observer (Subject * mod, int excLevel)
    {
        subj = mod;
        excitementLevel = excLevel;
        // Gli osservatori si registrano / si attaccano al Soggetto
        subj-> attach (questo);
    }
    virtual void update () = 0;
  protetta:
    Oggetto * getSubject () {
       return subj;
    }
    void setExcitementLevel (int excLevel) {
       excitementLevel = excLevel;
    }
    int getExcitementLevel () {
       ritorno eccitazione Livello;
    }
};

Questa è la dichiarazione SubSoggect () e, come abbiamo già detto prima, il suo compito è di informare tutti gli osservatori per aggiornare il loro stato.

void Subject :: notify () {
  per (int i = 0; i  update ();
}

Osservatori concreti
Gli osservatori concreti ereditano dalla classe Observer e devono tutti avere la funzione di aggiornamento. In questo esempio, gli osservatori concreti si distinguono tra giovani e vecchi sostenitori. Se il loro livello di eccitazione aumenta troppo, i sostenitori più anziani hanno il rischio di infarti e quelli più giovani hanno il rischio di bere e guidare. Il loro stato si aggiorna indipendentemente, come dimostreremo nella funzione principale più avanti.

class Old_ConcreteObserver: osservatore pubblico
{
   pubblico:
     // Chiama il costruttore principale per registrarsi con soggetto
     Old_ConcreteObserver (Subject * mod, int div)
        : Observer (mod, div) {}
     // Per le persone anziane, se il livello di eccitazione
     // è oltre 150 corrono il rischio di infarto
     void update ()
     {
        bool segnato = getSubject () -> getScored ();
        setExcitementLevel (getExcitementLevel () + 1);
        if (segnato && getExcitementLevel ()> 150)
        {
          cout << "La squadra del vecchio osservatore ha segnato !!"
               << "Il suo livello di eccitazione è"
               << getExcitementLevel ()
               << "attenzione agli attacchi di cuore!" << endl;
        }altro{
          cout << "La squadra non ha segnato. Yeeeih niente di cui preoccuparsi"
               << endl;
        }
    } // end update ()
};
class Young_ConcreteObserver: osservatore pubblico
{
   pubblico:
     // Chiama il costruttore principale per registrarsi con soggetto
     Young_ConcreteObserver (Subject * mod, int div)
       : Observer (mod, div) {}
     // Per le persone anziane, se il livello di eccitazione
     // è oltre 100 corrono il rischio di infarto
     void update ()
     {
        bool segnato = getSubject () -> getScored ();
        setExcitementLevel (getExcitementLevel () + 1);
        if (segnato && getExcitementLevel ()> 100)
        {
          cout << "La squadra di Young Observer ha segnato !!"
               << "Il suo livello di eccitazione è"
               << getExcitementLevel ()
               << "non bere e guidare !!" << endl;
        }altro{
          cout << "La squadra non ha segnato. Yeeh niente di cui preoccuparsi"
               << endl;
       }
    } // end update ()
};

Funzione principale
Gli osservatori concreti si registrano all'istanza del soggetto. Il loro stato è il livello di eccitazione che è il secondo parametro. Quando viene attivato l'evento "subj.setScored (true)", viene chiamato Subject :: notification () per aggiornare gli osservatori registrati. Nello scenario seguente, abbiamo tre osservatori, il giovane Obs1 è sovraeccitato e corre il rischio di bere e guidare, il vecchio Obs1 è anche sovraeccitato e corre un rischio diverso (di infarto). Infine, youngObs2 che è anche giovane come il primo non ha nulla di cui preoccuparsi poiché non è sovraeccitato.

È importante notare che i tre osservatori si sono aggiornati indipendentemente in base al loro stato (livello di eccitazione) e al loro tipo (giovane o vecchio).
int main () {
   Soggetto soggetto;
   Young_ConcreteObserver youngObs1 (& subj, 100);
   Old_ConcreteObserver oldObs1 (& subj, 150);
   Young_ConcreteObserver youngObs2 (& subj, 52);
   subj.setScored (true);
}
// Produzione
// La squadra di Young Observer ha segnato !! Il suo livello di eccitazione è 101
// non bere e guidare !!
// Ha segnato la squadra del vecchio osservatore !! Il suo livello di eccitazione è di 151 orologi
// fuori dagli attacchi di cuore! La squadra non ha segnato.
// Yeeh niente di cui preoccuparsi

Ci sono alcuni vantaggi per l'uso del pattern Observer e alcuni punti da notare quando questo pattern deve essere affrontato [Learning Python Design Patterns].

  • Il modello Observer fornisce un disegno in cui soggetto e osservatore sono liberamente accoppiati. Non è necessario che l'argomento sia a conoscenza della classe ConcreteObserver. Qualsiasi nuovo osservatore può essere aggiunto in qualsiasi momento. Non è necessario modificare l'oggetto quando viene aggiunto un nuovo osservatore. Gli osservatori e i soggetti non sono legati e indipendenti l'uno dall'altro, pertanto i cambiamenti nel Soggetto o nell'Osservatore non si influenzeranno a vicenda.
  • Non esiste alcuna opzione per la composizione, poiché l'interfaccia Observer può essere istanziata.
  • Se Observer viene utilizzato in modo improprio, può facilmente aggiungere complessità e portare a problemi di prestazioni.
  • Le notifiche possono essere inaffidabili e possono comportare condizioni di gara o incoerenze.

Il prossimo blog sarà una guida rapida al modello di progettazione di Bridge. È un modello di progettazione strutturale che viene utilizzato molto nel settore. Non dimenticare di mettere mi piace / applaudire al mio post sul blog e seguire il mio account. Questo per darmi la soddisfazione di aver aiutato alcuni colleghi sviluppatori e di spingermi a continuare a scrivere. Se esiste un modello di progettazione specifico che desideri conoscere, fammelo sapere in modo che possa fornirlo per te in futuro.

Altre guide rapide sui modelli di progettazione:

  1. Design Patterns: una breve guida a Abstract Factory.
  2. Design Patterns - Una guida rapida a Bridge Pattern.
  3. Design Patterns: una guida rapida al Builder Pattern.
  4. Design Patterns: una guida rapida al Decorator Pattern.
  5. Design Patterns: una guida rapida al modello di facciata.
  6. Design Patterns - Una guida rapida al modello Observer.
  7. Design Patterns: una breve guida a Singleton Pattern.