Le principe du motif de conception Sujet observateur est de permettre à une classe Sujet d’observer certaines événements et d’en notifier des observateurs qu’elle aura préalablement attachés afin qu’ils réagissent à l’événement
Concrètement, ce design patter participe donc à la réduction du couplage applicatif en permettant à chaque objet d’avoir une responsabilité unique.
Principe du design pattern Subject Observer
// A la détéction d'un événement le sujet itère sur les observateurs // et appelle leur méthode update class Subject { public function notify() { foreach ($observers as $observer) { $observer->update($this); } } }
Le SPL facilite l’implémentation en mettant à disposition deux interfaces spécifiques à ce design pattern :
- SplSubject
- SplObserver
pour implémenter respectivement le design pattern Subject et le design pattern Observer
Le Spl met aussi à disposition la classe SplObjectStorage qui permet le stockage d’objet de manière triviale
Un cas d’exemple classique serait un système de log par exemple. Imaginons qu’à chaque fois qu’une information doit être loguée, elle doit l’être sur plusieurs supports (fichier plat, base de données, ticket d’un gestionnaire de tickets, webservice, etc.), chaque support sera donc un observateur et le gestionnaire de logs sera le sujet
Exemple d’implémentation du sujet
<?php class Log implements SplSubject { const FORMAT = '%s : %s'; private $observers = null; private $msg; public function __construct() { $this->observers = new SplObjectStorage(); } public function log($msg) { $this->msg = sprintf(self::FORMAT, date('Y-m-d h:i:s', time()), $msg); $this->notify(); } public function attach(SplObserver $observer) { $this->observers->attach($observer); } public function detach(SplObserver $observer) { $this->observers->detach($observer); } public function notify() { foreach ($this->observers as $observer){ $observer->update($this); } } }
Cet exemple symbolise la classe Log se comportant comme Subject, elle implémente l’interface SplSubject qui l’oblige à définir trois méthodes (attach, detach et notify) qui permettent respectivement d’ajouter un ou plusieurs observateurs (Observer), de les supprimer et enfin de les notifier d’un événement intervenu.
La classe SplObjectStorage est utilisée afin de stocker les observateurs.
La méthode log permet ici de « journaliser » un message simple. Cette méthode se contente d’enregistrer le message à « journaliser » et d’en notifier les observateurs en invoquant leur méthode update
Exemple d’implémentation des observateurs
<?php class WriterFile implements SplObserver { private $file; public function __construct($path) { $this->file = $path; } public function update(SplSubject $subject) { file_put_contents($this->file, $subject.PHP_EOL); } } class WriterDatabase implements SplObserver { /* * corps de la classe */ }
Dans cet exemple, seule la classe WriterFile est détaillé pour comprendre le fonctionnement, la classe WriterDatabase ne sert qu’à comprendre qu’un sujet est capable de notifier tous les observateurs qui lui sont attachés. Chaque classe observatrice implémente l’interface SplObserver qui oblige la définition de la méthode update. Cette méthode est la seule connue par le sujet(Subject)
Exemple d’utilisation
$logger = new Log; $writeFile = new WriterFile(dirname(__FILE__). '/log/exemple.log'); $writeDatabase = new WriterDatabase(/*args*/); $logger->attach($writerFile); $logger->attach($writerDatabase); $logger->log('un message');
Ceci illustre le découplage : le log se fait simplement en appelant lé méthode log de l’objet $logger qui se charge, lui, de notifier chaque observateur en charge de journaliser ce message sur différents supports (fichier, base de données)