Modele de proiectare - Un ghid rapid pentru modelul de observator.

Modelul de observare este un tipar foarte frecvent utilizat. De fapt, este atât de comună încât se standardizează în multe limbaje / biblioteci de programare. În Java, există injava.util.Observer (învechit în Java 9). În Python este la fel de aproape ca apip instala modelul-observator. În C ++, putem folosi uneori biblioteca boost, mai precis #include . Cu toate acestea, este utilizat pe scară largă în industrie ca soluție personalizată. Pentru a-l putea folosi corect și a-i înțelege complexitatea, trebuie să-l scufundăm și să-l explorăm.

Modelul de observare este clasificat printre modelele de design comportamental. Modelele de proiectare comportamentală sunt cele mai specifice în legătură cu comunicarea între clase / obiecte. [după modelele de design explicate simplu]

Ce este un model de observator? În afară de un monitor de mers care transmite televiziune analogică (ca în imagine). Scopul modelului este de a defini o relație unu-la-mulți, astfel încât atunci când un obiect schimbă starea, celelalte sunt notificate și actualizate automat. Mai precis, dorește să fie informat cu privire la evenimentele petrecute în sistem. Permiteți să pună piesele puzzle-ului împreună în trei pași.

Pasul 1 - Cuvinte cheie

Definirea cuvintelor cheie este rețeta secretă din această serie de ghiduri rapide. Această metodă m-a ajutat să înțeleg cu adevărat modelele de design, să le codează în minte și să înțeleg diferențele dintre alte modele de design.

  1. Subiect: Este considerat drept deținătorul informațiilor, al datelor sau al logicii de afaceri.
  2. Înregistrare / atașare: observatorii se înregistrează la subiect, deoarece vor să fie anunțați atunci când există o schimbare.
  3. Eveniment: Evenimentele acționează ca un declanșator în subiect, astfel încât toți observatorii sunt notificați.
  4. Notificare: În funcție de implementare, subiectul poate „împinge” informații către observatori sau, observatorii pot „trage” dacă au nevoie de informații de la subiect.
  5. Actualizare: Observatorii își actualizează starea independent de alți observatori, însă starea lor s-ar putea schimba în funcție de evenimentul declanșat.

Pasul 2 - diagrama

Vă permite să împărțiți acest design în clase diferite pentru a simplifica puțin acest lucru.

  • ConcreteObservers sunt clase care conțin informații specifice instanței actuale. Funcția de actualizare se numește prin operațiunea de notificare () a subiectului. Observatorii se actualizează independent pe baza stării lor actuale.
  • Observatorul este clasa părinte a observatorilor concreți. Conține o instanță de subiect. Când un observator este inițializat, se înregistrează / se atașează de subiect.
  • Clasa de subiect are o listă sau o colecție de observatori. Când un eveniment este declanșat, acesta apelează operațiunea de notificare () care folosește toți observatorii apelând la funcția de actualizare.

Pasul 3 - Cod după exemplu

V-aș sugera să copiați clasa de cod pe clasă din depozitul meu de git „Andreas Poyias” sau fragmentele de mai jos (în ordinea prevăzută) și să-l lipiți în oricare dintre editorii C ++ online disponibili, cum ar fi c ++ shell, jdoodle, onlineGDB și rulați acesta pentru a observa ieșirea. Apoi citiți comentariile sau descrierea de mai jos. Luați-vă timp, citindu-l în detaliu (asta înseamnă un minut, nu mai puțin și nu mai mult).

Exemplu: Luați în considerare un joc de fotbal. Mulți suporteri urmăresc jocul. Împărțim suporterii în două categorii pe vârste, tineri și bătrâni. Când echipa lor înscrie un gol, suporterii reacționează diferit în funcție de vârstă și de nivelul lor de emoție.
Acum, să vorbim cu termenii folosiți pentru modelul de observator:

  • Jocul este subiectul, iar suporterii sunt observatorii.
  • Toți observatorii sunt atașați / înregistrați la subiect și sunt anunțați când echipa lor de fotbal (evenimentul declanșator este dacă echipa lor înscrie).
  • Observatorii își actualizează comportamentul în funcție de notificarea primită.

Subiect
Pentru această clasă, avem nevoie de acces la o listă de observatori. Când observatorii sunt pe punctul de a se înregistra, ei apelează funcția theattach (this) pentru a se adăuga la lista disponibilă (aceasta este o instanță a observatorului). Când un eveniment este declanșat, wenotify () toți observatorii să-și actualizeze în mod independent starea. În acest exemplu, declanșatorul este dacă echipa de fotbal a observatorului a marcat.

#include 
#include 
folosirea spațiului de nume std;
class Subiect {
    vector  observatori;
    bool marcat; // eveniment declansator
public:
    // înregistrați observatorii
    void attach (Observer * obs) {
        observers.push_back (obs);
    }
   
   // Acesta este EVENIMENTUL
   // setați dacă a fost marcat și anunțați TOATE observatorii
   void setScored (Scor bool) {
      punctat = Scor;
      notifica ();
   }
bool getScored () {
      randament marcat;
   }
   // notificarea implementării este mai jos
   // astfel încât scriptul să compileze și să ruleze
   anulare notificare ();
};

Observator
Această clasă depinde de subiectul cu care este înregistrată. Atunci când observatorii de beton sunt inițializați, ei se atașează de Subiect. În acest exemplu, starea fiecărui observator este emoția sa.

Clasa Observator
{
    Subiect * subj;
    int excitationLevel; // stat
  public:
    Observator (subiect * mod, int excLevel)
    {
        subj = mod;
        excitationLevel = excLevel;
        // Observatorii se înregistrează / se atașează de subiect
        subj-> atașați (aceasta);
    }
    actualizare virtuală () = 0;
  protejat:
    Subiect * getSubject () {
       returnare subj;
    }
    void setExcitationLevel (int excLevel) {
       excitationLevel = excLevel;
    }
    int getExcitationLevel () {
       întoarceți emoțiaLevel;
    }
};

Aceasta este declarația Subiect :: notifica () și așa cum am menționat înainte de sarcina sa este de a notifica tuturor observatorilor să își actualizeze starea.

void Subiect :: notificare () {
  for (int i = 0; i  actualizare ();
}

Observatori de beton
Observatorii concrete moștenesc din clasa Observer și toți trebuie să aibă funcția de actualizare. În acest exemplu, observatorii concreți se disting între susținătorii tineri și cei vechi. Dacă nivelul lor de excitare devine prea ridicat, suporterii mai în vârstă au riscul de atacuri de cord, iar cei mai tineri riscă să bea și să conducă. Actualizările stării lor sunt independente, după cum vom demonstra în funcția principală mai jos.

clasa Old_ConcreteObserver: public Observer
{
   public:
     // Solicită constructorul părinte să se înregistreze cu subiect
     Old_ConcreteObserver (Subiect * mod, int div)
        : Observator (mod, div) {}
     // Pentru persoanele în vârstă, dacă nivelul de emoție
     // are peste 150 de riscuri de atac de cord
     nul actualizare ()
     {
        bool marcat = getSubject () -> getScored ();
        setExcitationLevel (getExcitationLevel () + 1);
        if (marcat && getExcitationLevel ()> 150)
        {
          cout << "Echipa Old Observer a marcat !!"
               << „Nivelul său de emoție este”
               << getExcitationLevel ()
               << „Fii atent la atacurile de cord!” << endl;
        } Else {
          cout << "Echipa nu a marcat. Yeeeih nimic de care să vă faceți griji"
               << endl;
        }
    } // actualizare finală ()
};
clasa Young_ConcreteObserver: public Observer
{
   public:
     // Solicită constructorul părinte să se înregistreze cu subiect
     Young_ConcreteObserver (Subiect * mod, int div)
       : Observator (mod, div) {}
     // Pentru persoanele în vârstă, dacă nivelul de emoție
     // are peste 100 de riscuri de atac de cord
     nul actualizare ()
     {
        bool marcat = getSubject () -> getScored ();
        setExcitationLevel (getExcitationLevel () + 1);
        if (marcat && getExcitationLevel ()> 100)
        {
          cout << "Echipa tânărului observator a marcat !!"
               << „Nivelul său de emoție este”
               << getExcitationLevel ()
               << "Nu bea și nu conduce!" << endl;
        } Else {
          cout << "Echipa nu a marcat. Da, nimic nu vă faceți griji"
               << endl;
       }
    } // actualizare finală ()
};

Functie principala
Observatorii concreți se înregistrează la instanța Subiect. Starea lor este nivelul de entuziasm care este al doilea parametru. Când evenimentul este declanșat „subj.setScored (adevărat)”, atunci Subject :: notification () este chemat să actualizeze observatorii înregistrați. În scenariul de mai jos, avem trei observatori, tânăraObs1 este supraexcitată și riscă să bea și să conducă, vechiObs1 este, de asemenea, suprasolicitat, un risc diferit (de atac de cord). În cele din urmă, youngObs2 care este și tânăr ca primul nu are de ce să-și facă griji, deoarece nu este supraexcitat.

Este important să observăm că cei trei observatori au actualizat independent în funcție de starea lor (nivelul de emoție) și de tipul lor (tineri sau bătrâni).
int main () {
   Subiect subj;
   Young_ConcreteObserver youngObs1 (& subj, 100);
   Old_ConcreteObserver oldObs1 (& subj, 150);
   Young_ConcreteObserver youngObs2 (& subj, 52);
   subj.setScored (true);
}
// Iesire
// Echipa Young Observer a marcat !! Nivelul său de emoție este de 101
// nu bea și conduce!
// Echipa Old Observer a marcat !! Nivelul său de emoție este de 151 de ceasuri
// din atacuri de cord! Echipa nu a marcat.
// Da, nimic de care să vă faceți griji

Există câteva avantaje pentru utilizarea modelului Observer și câteva puncte de remarcat atunci când este abordat acest model [Învățarea modelelor de design Python].

  • Tiparul de observator oferă un design în care subiectul și observatorul sunt cuplate slab. Subiectul nu trebuie să știe despre clasa ConcreteObserver. Orice observator nou poate fi adăugat în orice moment. Nu este necesară modificarea subiectului atunci când este adăugat un nou Observator. Observatorii și subiecții nu sunt legați și sunt independenți unul de altul, prin urmare, modificările subiectului sau observatorului nu se vor afecta reciproc.
  • Nu există nicio opțiune pentru compoziție, deoarece interfața Observer poate fi inițiată.
  • Dacă Observatorul este folosit greșit, poate adăuga cu ușurință complexitatea și poate duce la probleme de performanță.
  • Notificările pot fi nesigure și pot duce la condiții de cursă sau inconsistență.

Următorul blog va fi un ghid rapid pentru modelul de proiectare Bridge. Este un model de design structural, care este utilizat destul de mult în industrie. Nu uitați să vă place / aplaudați pe blogul meu și să urmați contul meu. Aceasta înseamnă să-mi ofer satisfacția că am ajutat unii colegi dezvoltatori și să mă împingă să continui să scriu. Dacă există un model de design specific despre care ai vrea să înveți, anunță-mă, astfel încât să-l pot oferi în viitor.

Alte ghiduri rapide privind modelele de proiectare:

  1. Modele de proiectare - Un ghid rapid pentru Abstract Factory.
  2. Modele de proiectare - Un ghid rapid pentru modelul Bridge.
  3. Modele de proiectare - Un ghid rapid pentru modelul Builder.
  4. Modele de proiectare - Un ghid rapid pentru modelul de decorare.
  5. Modele de proiectare - Un ghid rapid pentru modelul fațadei
  6. Modele de proiectare - Un ghid rapid pentru modelul de observare.
  7. Modele de proiectare - Un ghid rapid pentru modelul Singleton.