Pattern Observer
Fa parte della sezione Programmazione orientata agli oggetti del percorso C++ di Coddy. Lezione 94 di 104.
Il pattern Observer definisce una dipendenza uno-a-molti tra oggetti: quando un oggetto (il soggetto) cambia stato, tutti i suoi dipendenti (gli osservatori) vengono notificati automaticamente. È ideale per i sistemi di eventi, gli aggiornamenti dell'interfaccia utente o qualsiasi scenario in cui più oggetti devono reagire ai cambiamenti.
Il pattern coinvolge due ruoli chiave: un Soggetto che mantiene un elenco di osservatori e li notifica, e gli Osservatori che implementano un'interfaccia di aggiornamento:
#include <iostream>
#include <vector>
#include <algorithm>
class Observer {
public:
virtual void update(int value) = 0;
virtual ~Observer() = default;
};
class Subject {
std::vector<Observer*> observers;
int state = 0;
public:
void attach(Observer* obs) {
observers.push_back(obs);
}
void detach(Observer* obs) {
observers.erase(
std::remove(observers.begin(), observers.end(), obs),
observers.end());
}
void setState(int value) {
state = value;
notify();
}
void notify() {
for (Observer* obs : observers) {
obs->update(state);
}
}
};
class Display : public Observer {
std::string name;
public:
Display(const std::string& n) : name(n) {}
void update(int value) override {
std::cout << name << " received: " << value << "\n";
}
};
int main() {
Subject sensor;
Display screen1("Screen1"), screen2("Screen2");
sensor.attach(&screen1);
sensor.attach(&screen2);
sensor.setState(42); // Entrambi i display sono stati avvisati
}Quando viene chiamato setState(), il soggetto scorre tutti gli osservatori registrati e chiama il loro metodo update(). Gli osservatori possono collegarsi o scollegarsi in qualsiasi momento, rendendo il sistema flessibile e debolmente accoppiato.
Usa Observer quando le modifiche a un oggetto richiedono l'aggiornamento di altri e non vuoi che questi oggetti siano strettamente accoppiati tra loro.
Sfida
FacileCostruiamo un sistema di monitoraggio della stazione meteorologica usando il pattern Observer. Creerai una stazione meteorologica che monitora i dati della temperatura e notifica automaticamente più unità di visualizzazione ogni volta che la temperatura cambia: una classica applicazione reale di questo pattern.
Organizzerai il codice in tre file:
Observer.h: Definisci l’interfaccia dell’observer e un observer concreto per la visualizzazione.Crea una classe astratta
Observercon un metodo virtuale puroupdate(double temperature)che gli observer implementano per ricevere notifiche, insieme a un distruttore virtuale.Poi crea una classe
TemperatureDisplayche eredita daObserver. Ogni display ha un nome (stringa) impostato tramite il costruttore. Quando viene chiamatoupdate(), dovrebbe stampare:[DisplayName]: Temperature is [temperature] degreesWeatherStation.h: Crea il subject che mantiene la temperatura e notifica gli observer.La tua classe
WeatherStationdovrebbe:- Memorizzare un elenco di puntatori agli observer e la temperatura corrente (inizializzata a
0.0) - Avere un metodo
attach(Observer* obs)per registrare gli observer - Avere un metodo
detach(Observer* obs)per rimuovere gli observer - Avere un metodo
setTemperature(double temp)che aggiorna la temperatura e notifica tutti gli observer collegati - Avere un metodo privato
notify()che chiamaupdate()su ciascun observer passando la temperatura corrente
- Memorizzare un elenco di puntatori agli observer e la temperatura corrente (inizializzata a
main.cpp: Mostra il pattern Observer in azione.Leggi tre input:
- Il nome del primo display (stringa)
- Il nome del secondo display (stringa)
- Un valore di temperatura (double)
Crea una
WeatherStatione due oggettiTemperatureDisplaycon i nomi forniti. Collega entrambi i display alla stazione, poi imposta la temperatura sul valore di input. Entrambi i display dovrebbero ricevere automaticamente l’aggiornamento e stampare i propri messaggi.Dopodiché, scollega il primo display e imposta una nuova temperatura superiore di 5 gradi rispetto al valore di input. Solo il secondo display dovrebbe ricevere questo aggiornamento.
Per esempio, con gli input Kitchen, Bedroom e 22.5:
Kitchen: Temperature is 22.5 degrees
Bedroom: Temperature is 22.5 degrees
Bedroom: Temperature is 27.5 degreesCon gli input Office, Lobby e 18.0:
Office: Temperature is 18 degrees
Lobby: Temperature is 18 degrees
Lobby: Temperature is 23 degreesNota come il pattern Observer crea un sistema debolmente accoppiato: la stazione meteorologica non deve sapere nulla dei display specifici, si limita a notificare chiunque sia in ascolto. Quando scolleghi un observer, smette automaticamente di ricevere aggiornamenti.
Provalo tu
#include <iostream>
#include <string>
#include "WeatherStation.h"
using namespace std;
int main() {
// Leggi i dati di input
string display1Name, display2Name;
double temperature;
cin >> display1Name;
cin >> display2Name;
cin >> temperature;
// TODO: Crea un oggetto WeatherStation
// TODO: Crea due oggetti TemperatureDisplay con i nomi forniti in input
// TODO: Collega entrambi i display alla stazione meteorologica
// TODO: Imposta la temperatura sul valore fornito in input
// (Entrambi i display dovrebbero stampare i propri messaggi)
// TODO: Scollega il primo display
// TODO: Imposta la temperatura a 5 gradi in più rispetto al valore fornito in input
// (Solo il secondo display dovrebbe stampare)
return 0;
}
Questa lezione include un breve quiz. Inizia la lezione per rispondere e tenere traccia dei tuoi progressi.
Tutte le lezioni di Programmazione orientata agli oggetti
1Fondamenti della programmazione orientata agli oggetti
File esterniBuild e compilazione in C++File header e file sorgenteNamespace e ambitoIntroduzione alla programmazione orientata agli oggetti in C++Classi e oggetti a confrontoIl puntatore 'this'Metodi (funzioni membro)Attributi (membri dati)Fondamenti di costruttori e distruttoriRiepilogo - Calcolatrice semplice4Proprietà delle classi
Membri di istanza e staticiGetter e setterFunzioni membro constParola chiave mutableMetodi e variabili staticiFunzioni e classi friendRiepilogo - Gestore di conti bancari7Ereditarietà
Ereditarietà di baseLivelli di accesso nell’ereditarietàOrdine di chiamata di costruttori e distruttoriRidefinizione dei metodiFunzioni virtuali e VTableEreditarietà multiplaEreditarietà virtualeRiepilogo - Gerarchia dei dipendenti10Panoramica della STL
Panoramica e filosofia della STLContenitori STLIteratoriAlgoritmi STLFuntori ed espressioni lambdaRiepilogo - Frequenza delle parole13Design Pattern Parte 1
Introduzione ai Design PatternPattern SingletonFactory e Abstract FactoryPattern BuilderPattern ObserverPattern Strategy2Gestione della memoria
Memoria Stack vs HeapPuntatori e riferimentiMemoria dinamica (new/delete)Puntatori intelligenti in C++RAII in C++Riepilogo - Gestore di array dinamico5Incapsulamento
Specificatori di accesso in C++Specificatori di accesso in dettaglioOccultamento delle informazioniStruct vs classClassi annidate e interneRiepilogo - Sistema di registrazione degli studenti8Polimorfismo
Polimorfismo a compile time e a runtimeOverload delle funzioniFunzioni virtuali: ripassoFunzioni virtuali pureClassi astratteProgettazione delle interfacce in C++Dynamic casting e RTTIRipasso: calcolatrice di forme11Concetti avanzati di OOP
Composizione vs ereditarietàMixin tramite CRTPIdiom PimplType ErasureEnum class e tipizzazione forteGestione delle eccezioni in OOPGerarchie personalizzate di eccezioni14Pattern di progettazione - Parte 2
Pattern CommandPattern AdapterPattern DecoratorPattern Template MethodPattern StatePattern CompositeRAII come pattern3Costruttori e distruttori
Costruttore predefinitoCostruttore con parametriCostruttore di copiaCostruttore di spostamentoListe di inizializzazione dei costruttoriCostruttori delegantiApprofondimento sui distruttoriRegola del Tre / Cinque / ZeroRipasso - classe String6Sovraccarico degli operatori
Introduzione al sovraccarico degli operatoriSovraccarico degli operatori aritmeticiSovraccarico degli operatori di confrontoOperatori di flussoSovraccarico dell'operatore di assegnazioneSovraccarico degli operatori [] e ()Operatori di conversione di tipoRipasso - Classe Matrix9Template
Template di funzioneTemplate di classeSpecializzazione dei templateTemplate variadiciBasi di SFINAE e dei trait di tipoRiepilogo - Contenitore genericoEsercitati da solo: Compilatore C++ online