Patron Observateur
Fait partie de la section Programmation Orientée Objet du Journey C++ de Coddy — leçon 94 sur 104.
Le patron de conception Observateur définit une dépendance de type un-à-plusieurs entre des objets : lorsqu'un objet (le sujet) change d'état, tous ses dépendants (les observateurs) sont notifiés automatiquement. C'est idéal pour les systèmes d'événements, les mises à jour d'interface utilisateur (UI), ou tout scénario où plusieurs objets doivent réagir à des changements.
Le patron implique deux rôles clés : un Subject qui maintient une liste d'observateurs et les notifie, et des Observers qui implémentent une interface de mise à jour :
#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); // Les deux afficheurs sont notifiés
}Lorsque setState() est appelé, le sujet parcourt tous les observateurs enregistrés et appelle leur méthode update(). Les observateurs peuvent s'attacher ou se détacher à tout moment, ce qui rend le système flexible et faiblement couplé.
Utilisez le pattern Observateur lorsque les modifications apportées à un objet nécessitent la mise à jour d'autres objets, et que vous ne voulez pas que ces objets soient fortement couplés entre eux.
Défi
FacileConstruisons un système de surveillance de Station Météo en utilisant le patron de conception Observer (Observateur). Vous allez créer une station météo qui suit les données de température et informe automatiquement plusieurs unités d'affichage chaque fois que la température change — une application concrète et classique de ce patron.
Vous organiserez votre code sur trois fichiers :
Observer.h: Définissez l'interface de l'observateur et un observateur d'affichage concret.Créez une classe abstraite
Observeravec une méthode virtuelle pureupdate(double temperature)que les observateurs implémentent pour recevoir les notifications, ainsi qu'un destructeur virtuel.Ensuite, créez une classe
TemperatureDisplayqui hérite deObserver. Chaque affichage possède un nom (string) défini via son constructeur. Lorsqueupdate()est appelée, elle doit afficher :[DisplayName]: Temperature is [temperature] degreesWeatherStation.h: Créez le sujet qui maintient la température et notifie les observateurs.Votre classe
WeatherStationdoit :- Stocker une liste de pointeurs d'observateurs et la température actuelle (initialisée à
0.0) - Avoir une méthode
attach(Observer* obs)pour enregistrer les observateurs - Avoir une méthode
detach(Observer* obs)pour supprimer les observateurs - Avoir une méthode
setTemperature(double temp)qui met à jour la température et notifie tous les observateurs attachés - Avoir une méthode privée
notify()qui appelleupdate()sur chaque observateur avec la température actuelle
- Stocker une liste de pointeurs d'observateurs et la température actuelle (initialisée à
main.cpp: Démontrez le patron Observer en action.Lisez trois entrées :
- Le nom du premier affichage (string)
- Le nom du deuxième affichage (string)
- Une valeur de température (double)
Créez une
WeatherStationet deux objetsTemperatureDisplayavec les noms fournis. Attachez les deux affichages à la station, puis réglez la température sur la valeur d'entrée. Les deux affichages doivent automatiquement recevoir la mise à jour et afficher leurs messages.Après cela, détachez le premier affichage et réglez une nouvelle température supérieure de 5 degrés à l'entrée. Seul le deuxième affichage doit recevoir cette mise à jour.
Par exemple, avec les entrées Kitchen, Bedroom, et 22.5 :
Kitchen: Temperature is 22.5 degrees
Bedroom: Temperature is 22.5 degrees
Bedroom: Temperature is 27.5 degreesAvec les entrées Office, Lobby, et 18.0 :
Office: Temperature is 18 degrees
Lobby: Temperature is 18 degrees
Lobby: Temperature is 23 degreesRemarquez comment le patron Observer crée un système à couplage faible — la station météo n'a pas besoin de connaître les détails des affichages spécifiques, elle informe simplement quiconque est à l'écoute. Lorsque vous détachez un observateur, il cesse automatiquement de recevoir les mises à jour.
Aide-mémoire
Le patron de conception Observateur (Observer pattern) définit une dépendance de un à plusieurs où un sujet notifie automatiquement tous ses observateurs lorsque son état change.
Le patron comporte deux rôles clés :
- Sujet (Subject) : Maintient une liste d'observateurs et les informe des changements d'état
- Observateur (Observer) : Implémente une interface de mise à jour pour recevoir les notifications
Implémentation de base :
#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";
}
};Méthodes clés :
attach(): Enregistre un observateur pour recevoir des notificationsdetach(): Supprime un observateur de la liste de notificationnotify(): Parcourt tous les observateurs et appelle leur méthodeupdate()
Utilisez le patron Observateur lorsque les modifications apportées à un objet nécessitent la mise à jour d'autres objets sans couplage fort entre eux.
Essayez vous-même
#include <iostream>
#include <string>
#include "WeatherStation.h"
using namespace std;
int main() {
// Lire les entrées
string display1Name, display2Name;
double temperature;
cin >> display1Name;
cin >> display2Name;
cin >> temperature;
// TODO: Créer un objet WeatherStation
// TODO: Créer deux objets TemperatureDisplay avec les noms saisis
// TODO: Attacher les deux afficheurs à la WeatherStation
// TODO: Régler la température à la valeur saisie
// (Les deux afficheurs doivent imprimer leurs messages)
// TODO: Détacher le premier afficheur
// TODO: Régler la température à 5 degrés de plus que la valeur saisie
// (Seul le deuxième afficheur doit imprimer)
return 0;
}
Cette leçon comprend un petit quiz. Commencez la leçon pour y répondre et suivre votre progression.
Toutes les leçons de Programmation Orientée Objet
1Fondamentaux de la POO
Fichiers externesBuild et Compilation C++Fichiers d'en-tête et Fichiers sourceEspaces de noms et PortéeIntroduction à la POO en C++Classes vs ObjetsLe pointeur 'this'Méthodes (Fonctions membres)Attributs (Membres de données)Bases des Ctors et DtorsRécapitulatif - Calculatrice simple4Propriétés de classe
Membres d'instance vs membres statiquesGetters et SettersFonctions membres constMot-clé mutableMéthodes et variables statiquesFonctions et classes amiesRécapitulatif - Gestionnaire de compte bancaire7Héritage
Héritage de baseNiveaux d'accès à l'héritageOrdre d'appel des Ctor et DtorRedéfinition de méthodesFonctions virtuelles et VTableHéritage multipleHéritage virtuelRécapitulatif - Hiérarchie des employés10Présentation de la STL
Présentation et philosophie de la STLConteneurs de la STLItérateursAlgorithmes de la STLFoncteurs et expressions lambdaRécapitulatif - Fréquence des mots13Patrons de conception, Partie 1
Introduction aux patrons de conceptionPatron SingletonFabrique & Fabrique abstraitePatron MonteurPatron ObservateurPatron Stratégie2Gestion de la mémoire
Mémoire Pile vs TasPointeurs et RéférencesMémoire dynamique (new/delete)Smart Pointers en C++RAII en C++Récapitulatif - Gestionnaire de tableaux dynamiques5Encapsulation
Spécificateurs d'accès en C++Spécificateurs d'accès en profondeurMasquage d'informationStruct vs ClassClasses imbriquées et internesRécapitulatif - Système de gestion d'étudiants8Polymorphisme
Polymorphisme : Compilation vs ExécutionSurcharge de fonctionsRetour sur les fonctions virtuellesFonctions virtuelles puresClasses abstraitesConception d'interfaces en C++Dynamic Casting & RTTIRécapitulatif - Calculateur de formes11Concepts avancés de la POO
Composition vs HéritageMixins via CRTPIdiome PimplEffacement de typeEnum Classes & Typage fortGestion des exceptions en POOHiérarchies d'exceptions personnalisées14Patrons de conception, Partie 2
Patron CommandePatron AdaptateurPatron DécorateurPatron Template MethodPatron ÉtatPatron CompositeRAII en tant que patron3Constructeurs et Destructeurs
Constructeur par défautConstructeur paramétréConstructeur de copieConstructeur de déplacementListes d'initialisation du constructeurConstructeurs déléguésAnalyse approfondie du destructeurRègle de trois / cinq / zéroRécapitulatif - Classe String6Surcharge d'opérateurs
Introduction à la surcharge d'opérateursSurcharge des opérateurs arithmétiquesSurcharge des opérateurs de comparaisonOpérateurs de fluxSurcharge de l'opérateur d'affectationSurcharge des opérateurs [] et ()Opérateurs de conversion de typeRécapitulatif - Classe Matrix9Templates
Templates de fonctionsTemplates de classesSpécialisation de templatesTemplates variadiquesBases de SFINAE & Type TraitsRécapitulatif - Conteneur générique