Observer

Observer

Le design pattern Observer est l’un des patrons de conception les plus utilisés en développement logiciel.

Il permet de définir une relation de dépendance entre un objet (appelé sujet) et plusieurs autres objets (appelés observateurs) afin que tout changement dans le sujet soit automatiquement notifié aux observateurs.

Cette approche favorise la modularité, la réutilisabilité et la maintenabilité du code.

Problème

Le design pattern Observer est utilisé pour résoudre le problème suivant: lorsqu’un objet subit des changements d’état, il peut y avoir d’autres objets qui doivent être informés de ces changements afin de prendre des mesures appropriées.

Sans Observer, l’objet qui effectue les changements devrait connaître tous les objets qui doivent être notifiés et comment les notifier. Cela peut entraîner une forte dépendance entre les objets, ce qui peut rendre le code difficile à maintenir et à étendre.

Solution

Le design pattern Observer propose une solution élégante à ce problème en séparant les objets qui effectuent les changements de ceux qui doivent être notifiés. Il introduit un sujet observable qui est responsable de notifier tous les observateurs lorsque son état change.

Les observateurs sont enregistrés auprès du sujet observable et peuvent être ajoutés ou supprimés dynamiquement. De cette façon, l’objet qui effectue les changements n’a pas à connaître les détails de la notification ou même l’existence des observateurs, réduisant ainsi la dépendance entre les objets et améliorant la maintenabilité du code.

Analogie

Le design pattern Observer peut être comparé à un abonnement à un magazine. Lorsque vous vous abonnez à un magazine, vous donnez votre adresse à la publication pour qu’elle puisse vous envoyer chaque nouveau numéro dès qu’il est publié. De la même manière, dans le pattern Observer, un objet (appelé sujet) maintient une liste d’observateurs intéressés par les changements de son état.

Lorsque l’état du sujet change, il informe automatiquement tous les observateurs de la liste, qui peuvent alors effectuer les actions appropriées en réponse à ce changement.

Tout comme vous pouvez vous désabonner d’un magazine si vous ne voulez plus recevoir de nouveaux numéros, les observateurs peuvent se désabonner du sujet s’ils ne sont plus intéressés par ses changements d’état.

Implémentation

La méthodologie d’implémentation du pattern observer peut être divisée en plusieurs étapes:

  • Identifier les objets impliqués: Tout d’abord, vous devez identifier l’objet observé (ou sujet) et les observateurs qui doivent être informés de tout changement dans l’objet.
  • Définir l’interface: Ensuite, vous devez définir une interface pour l’objet observé qui permettra aux observateurs de s’abonner pour recevoir des notifications de changement. Cette interface peut inclure des méthodes pour ajouter, supprimer et notifier les observateurs.
  • Implémenter l’interface: Vous pouvez ensuite implémenter l’interface de l’objet observé dans une classe concrète qui définit comment les abonnements et les notifications sont gérés.
  • Implémenter les observateurs: Ensuite, vous devez implémenter les observateurs qui seront abonnés à l’objet observé. Ces observateurs doivent également implémenter une interface qui définit comment ils recevront les notifications de changement.
  • Abonnement des observateurs: Vous pouvez alors abonner les observateurs à l’objet observé en utilisant la méthode d’abonnement définie dans l’interface de l’objet observé.
  • Notification des observateurs: Enfin, lorsqu’un changement est apporté à l’objet observé, celui-ci doit notifier tous les observateurs en utilisant la méthode de notification définie dans son interface. Les observateurs peuvent alors traiter la notification et effectuer des actions en conséquence.

En suivant cette méthodologie, vous pouvez implémenter le pattern observer de manière efficace et modulaire dans votre code, permettant ainsi une plus grande flexibilité et une meilleure réactivité à tout changement dans l’objet observé.

// L'interface pour l'objet observé
interface Observable {
  public function addObserver(Observer $observer);
  public function removeObserver(Observer $observer);
  public function notifyObservers();
}

// L'interface pour les observateurs
interface Observer {
  public function update(Observable $observable);
}

// L'objet observé (le magazine)
class Magazine implements Observable {
  private $name;
  private $subscribers = array();

  public function __construct($name) {
    $this->name = $name;
  }

  public function addObserver(Observer $observer) {
    $this->subscribers[] = $observer;
  }

  public function removeObserver(Observer $observer) {
    $key = array_search($observer, $this->subscribers, true);
    if ($key !== false) {
      unset($this->subscribers[$key]);
    }
  }

  public function notifyObservers() {
    foreach ($this->subscribers as $subscriber) {
      $subscriber->update($this);
    }
  }

  public function publishNewIssue() {
    // Code pour publier un nouvel exemplaire
    $this->notifyObservers();
  }

  public function getName() {
    return $this->name;
  }
}

// L'observateur (le lecteur)
class Reader implements Observer {
  private $name;

  public function __construct($name) {
    $this->name = $name;
  }

  public function update(Observable $observable) {
    echo "Nouvelle édition de " . $observable->getName() . " publiée! \n";
    echo $this->name . " va chercher sa copie. \n";
  }
}

// Création de l'objet observé (le magazine)
$magazine = new Magazine("Science & Vie");

// Création des observateurs (les lecteurs)
$reader1 = new Reader("Jean");
$reader2 = new Reader("Marie");

// Abonnement des observateurs à l'objet observé
$magazine->addObserver($reader1);
$magazine->addObserver($reader2);

// Publication d'un nouvel exemplaire du magazine (changement observé)
$magazine->publishNewIssue();

// Résultat : les observateurs sont notifiés du changement
// "Nouvelle édition de Science & Vie publiée! Jean va chercher sa copie."
// "Nouvelle édition de Science & Vie publiée! Marie va chercher sa copie."

Dans cet exemple, l’interface Observable définit les méthodes pour ajouter, supprimer et notifier les observateurs, tandis que l’interface Observer définit la méthode update qui sera appelée par l’objet observé pour informer l’observateur du changement.

La classe Magazine implémente l’interface Observable et maintient une liste d’observateurs abonnés. Lorsqu’un nouvel exemplaire est publié, la méthode notifyObservers() est appelée pour informer tous les observateurs abonnés.

La classe Reader implémente l’interface Observer et définit la méthode update() qui sera appelée par l’objet observé lorsqu’un changement se produit. Dans cet exemple, la méthode update() affiche simplement un message indiquant que le lecteur est en train de chercher sa copie.

Enfin, le code crée un objet Magazine, crée deux observateurs Reader, les abonne à l’objet Magazine, puis publie un nouvel exemplaire du magazine en appelant la méthode publishNewIssue(). Cette méthode appelle ensuite la méthode notifyObservers() pour informer tous les observateurs abonnés.

Lorsque la méthode notifyObservers() est appelée, elle boucle sur la liste des observateurs abonnés et appelle la méthode update() pour chaque observateur. Dans cet exemple, chaque observateur affiche un message indiquant qu’il va chercher sa copie.

Le résultat affiché est donc :

Nouvelle édition de Science & Vie publiée! Jean va chercher sa copie.
Nouvelle édition de Science & Vie publiée! Marie va chercher sa copie.

Ce qui montre que les deux observateurs ont été notifiés du changement et ont agi en conséquence.

Ce code illustre donc bien l’utilisation du pattern Observer pour permettre à un objet d’informer tous ses observateurs lorsqu’un changement se produit. Cela permet de maintenir une cohérence entre différents objets et de faciliter la communication entre eux.

Comments

No comments yet. Why don’t you start the discussion?

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *