Accueil / Tutoriels / Observer
Observer
Publié le 3 Mars 2025Observer (observateur en français) est un pattern comportemental qui va mettre en place un système d’abonnement pour des objets (les observateurs) qui pourront être notifiés lorsque des changements sont opérés sur un ou des objets particuliers (les sujets).
Le principe
Exemples d’utilisations
Websockets pour des notification en temps réel
Mode clair/sombre sur une page
Points d’attentions
Implémenter un observer
Exemple d'utilisation d'Observer en Go
package observerData
type User struct {
Name string
Email string
Phone string
}
package subject
import (
"sync"
"practice/designpatterns/observer/subscribers"
)
type Subject struct {
observers [ ]subscribers.Observer
mu sync.Mutex
}
func (s *Subject) Subscribe(o subscribers.Observer) {
s.mu.Lock()
defer s.mu.Unlock()
s.observers = append(s.observers, o)
}
func (s *Subject) Unsubscribe(o subscribers.Observer) {
s.mu.Lock()
defer s.mu.Unlock()
for i, observer := range s.observers {
if observer == o {
// Exemple pour supprimer l'observateur en recréant la slice sans l'élément si l'ordre est important
// s.observers = append(s.observers[:i], s.observers[i+1:]...)
s.observers[i] = s.observers[len(s.observers)-1]
s.observers = s.observers[:len(s.observers)-1]
break
}
}
}
func (s *Subject) Notify(event string, data interface{}) {
s.mu.Lock()
defer s.mu.Unlock()
// On crée une copie pour éviter les problèmes de concurrence.
observersCopy := make([ ]subscribers.Observer, len(s.observers))
copy(observersCopy, s.observers)
for _, observer := range observersCopy {
observer.Update(event, data)
}
}
package subscribers
type Observer interface {
Update(event string, data interface{})
}
package subscribers
import (
"log"
"practice/designpatterns/observer/observerData"
)
type EmailObserver struct{}
func (e *EmailObserver) Update(event string, data interface{}) {
if event == "UserRegistered" {
if user, ok := data.(observerData.User); ok {
log.Printf("[EmailObserver] E-mail envoyé à %s (%s)", user.Name, user.Email)
}
}
}
package subscribers
import (
"log"
"practice/designpatterns/observer/observerData"
)
type SMSObserver struct{}
func (s *SMSObserver) Update(event string, data interface{}) {
if event == "UserRegistered" {
if user, ok := data.(observerData.User); ok {
log.Printf("[SMSObserver] SMS envoyé à %s (%s)", user.Name, user.Phone)
}
}
}
package main
import (
"fmt"
"log"
"net/http"
"practice/designpatterns/observer/observerData"
"practice/designpatterns/observer/subject"
"practice/designpatterns/observer/subscribers"
)
func main() {
// Exemple de désinscription (vous pouvez l'appeler en fonction de la logique de votre application)
// registrationSubject.Subscribe(smsObserver)
// Configuration de l'endpoint HTTP.
http.HandleFunc("/register", registerHandler)
log.Println("Serveur démarré sur http://localhost:8080")
log.Fatal(http.ListenAndServe(":8080", nil))
}
func registerHandler(w http.ResponseWriter, r *http.Request) {
// On initialise les observers ici pour l'exemple et des raisons pratiques.
// Dans un vrai projet on aurait un service dédié.
// Sujet global pour la gestion de l'enregistrement des utilisateurs.
var registrationSubject = &subject.Subject{}
// Création des observateurs.
emailObserver := &subscribers.EmailObserver{}
smsObserver := &subscribers.SMSObserver{}
// Inscription des observateurs auprès du sujet.
registrationSubject.Subscribe(emailObserver)
registrationSubject.Subscribe(smsObserver)
// Code à mettre en place pour le traitement du formulaire d'inscription
// Ici je mets des données en dur
user := observerData.User{
Name: "Toto",
Email: "toto@mail.com",
Phone: "phone",
}
// Notifier tous les observateurs de l'événement d'inscription.
registrationSubject.Notify("UserRegistered", user)
fmt.Fprintf(w, "Utilisateur %s enregistré avec succès!", user.Name)
}
Le même exemple en PHP
namespace Practice\DesignPatterns\Observer;
interface ObserverInterface
{
public function update(string $event, $data);
}
namespace Practice\DesignPatterns\Observer;
class User {
public $name;
public $email;
public $phone;
public function __construct(string $name, string $email, string $phone) {
$this->name = $name;
$this->email = $email;
$this->phone = $phone;
}
}
namespace Practice\DesignPatterns\Observer;
class Subject {
private $observers = [ ];
// Ajoute un observateur.
public function subscribe(ObserverInterface $observer) {
$this->observers[ ] = $observer;
}
// Supprime un observateur.
public function unsubscribe(ObserverInterface $observer) {
foreach ($this->observers as $key => $obs) {
if ($obs === $observer) {
unset($this->observers[$key]);
}
}
}
// Notifie tous les observateurs de l'événement.
public function notify(string $event, $data) {
foreach ($this->observers as $observer) {
$observer->update($event, $data);
}
}
}
namespace Practice\DesignPatterns\Observer;
class Subject {
private $observers = [ ];
// Ajoute un observateur.
public function subscribe(ObserverInterface $observer) {
$this->observers[ ] = $observer;
}
// Supprime un observateur.
public function unsubscribe(ObserverInterface $observer) {
foreach ($this->observers as $key => $obs) {
if ($obs === $observer) {
unset($this->observers[$key]);
}
}
}
// Notifie tous les observateurs de l'événement.
public function notify(string $event, $data) {
foreach ($this->observers as $observer) {
$observer->update($event, $data);
}
}
}
namespace Practice\DesignPatterns\Observer;
class SMSObserver implements ObserverInterface {
public function update(string $event, $data) {
if ($event === "UserRegistered" && $data instanceof User) {
error_log("[SMSObserver] SMS envoyé à {$data->name} ({$data->phone})");
}
}
}
declare(strict_types=1);
require "./vendor/autoload.php";
// Instanciation du sujet pour gérer l'enregistrement des utilisateurs.
use Practice\DesignPatterns\Observer\EmailObserver;
use Practice\DesignPatterns\Observer\SMSObserver;
use Practice\DesignPatterns\Observer\Subject;
use Practice\DesignPatterns\ObserverUser;
$registrationSubject = new Subject();
// Création des observateurs et inscription auprès du sujet.
$emailObserver = new EmailObserver();
$smsObserver = new SMSObserver();
$registrationSubject->subscribe($emailObserver);
$registrationSubject->subscribe($smsObserver);
// Code à mettre en place pour le traitement du formulaire d'inscription
// Ici je mets des données en dur
$name = "Toto";
$email = "toto@mail.com";
$phone = "phone";
// Création d'un nouvel utilisateur.
$user = new User($name, $email, $phone);
// Notification des observateurs de l'événement "UserRegistered".
$registrationSubject->notify("UserRegistered", $user);
echo "Utilisateur " . htmlspecialchars($name) . " enregistré avec succès!";