CodeGym /Cours /JAVA 25 SELF /Patron de conception Observateur (Observer)

Patron de conception Observateur (Observer)

JAVA 25 SELF
Niveau 50 , Leçon 3
Disponible

1. Découverte du patron « Observateur »

Le patron « Observateur » (Observer) est l’un des patrons de conception les plus connus et fondamentaux. Il décrit une situation où un objet (observable, ou subject) signale ses changements à d’autres objets (observateurs, observers) qui se sont abonnés à ces changements.

Plus simplement : nous avons une chaîne Telegram (l’objet observable) et des « abonnés » (les observateurs). Chaque fois qu’un nouveau post sort, la chaîne notifie tous les abonnés, et ceux‑ci décident quoi en faire — lire, ignorer ou se désabonner.

En programmation, ce patron permet de notifier automatiquement les objets intéressés d’événements ou de changements d’état, sans les coupler directement entre eux. C’est essentiel pour construire des systèmes flexibles, extensibles et faciles à maintenir.

Où rencontre‑t‑on le patron « Observateur » ?

  • Dans les interfaces graphiques (Swing, AWT, JavaFX) — les écouteurs d’événements.
  • Dans les bibliothèques réactives (RxJava, Project Reactor).
  • Dans la logique métier : réaction aux changements d’état du modèle.
  • Dans les moteurs de jeu (événements de collisions, de victoire, de défaite, etc.).
  • Partout où il faut séparer « ce qui s’est passé » de « ce qu’il faut en faire ».

Lien du patron avec les événements et les écouteurs en Java

En fait, tout le modèle événementiel de Java est bâti sur l’« Observateur ». Lorsque vous écrivez button.addActionListener(listener);, vous appliquez ce patron :

  • Observable — le bouton (ou un autre composant).
  • Observateur — votre écouteur implémentant la méthode actionPerformed().
  • Événement — l’utilisateur a cliqué, a survolé avec la souris, etc.
  • Notification — le composant appelle actionPerformed().

Tout cela — une implémentation classique d’Observer !

2. Implémentation classique du patron « Observateur »

Voyons comment implémenter le patron avec nos propres classes — sans Swing ni AWT — afin de constater qu’il n’y a aucune magie.

Éléments principaux du patron

  • Observable (Subject) — l’objet observable. Il conserve la liste des observateurs et les notifie des changements.
  • Observer — l’interface d’observateur, généralement avec la méthode update().

Exemple : thermomètre et climatiseur

Interface d’observateur

public interface TemperatureObserver {
    void temperatureChanged(int newTemperature);
}

Classe « Thermomètre » (observable)

import java.util.*;

public class Thermometer {
    private int temperature;
    private final List<TemperatureObserver> observers = new ArrayList<>();

    public void addObserver(TemperatureObserver observer) {
        observers.add(observer);
    }

    public void removeObserver(TemperatureObserver observer) {
        observers.remove(observer);
    }

    public void setTemperature(int newTemperature) {
        if (this.temperature != newTemperature) {
            this.temperature = newTemperature;
            notifyObservers();
        }
    }

    private void notifyObservers() {
        for (TemperatureObserver observer : observers) {
            observer.temperatureChanged(temperature);
        }
    }
}

Exemple d’observateur — « Climatiseur »

public class AirConditioner implements TemperatureObserver {
    @Override
    public void temperatureChanged(int newTemperature) {
        if (newTemperature > 25) {
            System.out.println("Climatiseur allumé ! Il fait chaud : " + newTemperature + "°C");
        } else {
            System.out.println("Climatiseur éteint. Température : " + newTemperature + "°C");
        }
    }
}

Utilisation

public class Main {
    public static void main(String[] args) {
        Thermometer thermometer = new Thermometer();
        AirConditioner conditioner = new AirConditioner();

        thermometer.addObserver(conditioner);

        thermometer.setTemperature(22); // Climatiseur éteint. Température : 22°C
        thermometer.setTemperature(28); // Climatiseur allumé ! Il fait chaud : 28°C
    }
}

Et voilà, c’est toute la magie ! On peut ajouter encore des dizaines d’observateurs — ils recevront tous les notifications lors d’un changement de température.

Schéma du patron

flowchart LR
    T["Thermomètre (Observable)"] -- notifie --> AC["Climatiseur (Observer)"]
    T -- notifie --> L["Logger (Observer)"]
    T -- notifie --> Alarm["Alarme (Observer)"]

Détails modernes : Observable obsolète et nouvelles approches

Dans la bibliothèque standard de Java, java.util.Observable et java.util.Observer existaient, mais à partir de Java 9 ils sont marqués comme obsolètes (deprecated). La raison : manque de flexibilité (par exemple, Observable est une classe et non une interface, ce qui complique l’héritage d’une autre classe).

L’approche moderne consiste à concevoir vos propres interfaces d’écouteurs et la logique d’abonnement/désabonnement (comme dans l’exemple ci‑dessus). C’est plus flexible, plus sûr et mieux adapté aux besoins réels.

3. Exemple : mini‑application avec des abonnés

Réalisons un « compteur de clics » avec la possibilité de s’abonner aux changements de valeur.

Interface d’écouteur

public interface CounterListener {
    void counterChanged(int newValue);
}

Classe‑compteur

import java.util.*;

public class Counter {
    private int value = 0;
    private final List<CounterListener> listeners = new ArrayList<>();

    public void addCounterListener(CounterListener l) {
        listeners.add(l);
    }

    public void removeCounterListener(CounterListener l) {
        listeners.remove(l);
    }

    public void increment() {
        value++;
        notifyListeners();
    }

    private void notifyListeners() {
        for (CounterListener l : listeners) {
            l.counterChanged(value);
        }
    }

    public int getValue() {
        return value;
    }
}

Écouteur : afficher un message

public class ConsoleCounterListener implements CounterListener {
    @Override
    public void counterChanged(int newValue) {
        System.out.println("Le compteur a changé : " + newValue);
    }
}

Utilisation

public class Main {
    public static void main(String[] args) {
        Counter counter = new Counter();
        counter.addCounterListener(new ConsoleCounterListener());

        counter.increment(); // Le compteur a changé : 1
        counter.increment(); // Le compteur a changé : 2
    }
}

4. Subtilités utiles

Alternatives et extensions modernes

Dans les projets réels, on utilise souvent des classes anonymes ou des expressions lambda pour s’abonner : counter.addCounterListener(newValue -> System.out.println("Nouvelle valeur : " + newValue));

(Pour faire cela, l’interface doit être fonctionnelle — posséder une seule méthode abstraite.)

Les bibliothèques réactives (RxJava, Project Reactor) sont également populaires, où « Observateur » est implémenté avec prise en charge de flux d’événements, de la filtration, de l’asynchronisme, etc. Pour en saisir l’essence, le schéma classique vu ci‑dessus suffit.

Applications du patron « Observateur » dans la pratique

  • Modèles de données. Une modification du modèle (liste de tâches, produits, utilisateurs) notifie les vues pour mise à jour.
  • Journalisation. Un abonné « logger » réagit aux événements dans tout le système.
  • Notifications. Lors d’un changement d’état — envoi d’e‑mails, de notifications push, de messages dans Telegram.
  • Jeux. Changement de santé, apparition d’un ennemi, fin de niveau.
  • Multithreading. Un thread publie des événements, d’autres réagissent.

5. Erreurs courantes lors de l’implémentation du patron « Observateur »

Erreur n° 1 : oubli de supprimer l’écouteur. Si un écouteur n’est plus nécessaire mais n’a pas été supprimé, il continuera à recevoir des notifications. Dans les applications de longue durée, cela peut conduire à des fuites de mémoire.

Erreur n° 2 : opérations longues ou bloquantes dans les gestionnaires. Si un gestionnaire effectue un travail lourd (E/S, base de données), l’application peut « geler », surtout si les notifications proviennent du thread d’UI. Déplacez les tâches lourdes dans des threads en arrière‑plan.

Erreur n° 3 : exceptions dans les écouteurs. Une exception dans un écouteur peut interrompre la diffusion aux autres. Enveloppez les appels aux écouteurs dans un try-catch et journalisez les erreurs.

Erreur n° 4 : enregistrement multiple du même écouteur. Si le même écouteur est ajouté plusieurs fois, il recevra l’événement autant de fois. Surveillez l’enregistrement et empêchez les doublons.

Erreur n° 5 : couplage fort entre l’observable et l’observateur. Si l’observable connaît des implémentations concrètes des observateurs, on perd le faible couplage. Utilisez uniquement des interfaces (par exemple, TemperatureObserver, CounterListener).

1
Mission
JAVA 25 SELF, niveau 50, leçon 3
Bloqué
Fondation d'une agence TechNews 📰
Fondation d'une agence TechNews 📰
1
Mission
JAVA 25 SELF, niveau 50, leçon 3
Bloqué
Diffusion des actualités brûlantes 🌐
Diffusion des actualités brûlantes 🌐
Commentaires
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION