Qu’est-ce que le patron observateur ?
Le patron observateur permet à un objet de prévenir automatiquement d’autres objets quand il change. L’objet observé ne connaît pas leurs détails : il sait seulement qu’il doit les prévenir. Il évite que le code qui change une valeur ait à penser à tout ce qui en dépend.
La cafétéria suit son stock de sandwichs. Quand il baisse, le journal le note et une alerte prévient le gérant.
À la fin de cette leçon
Remarque
Je peux appliquer l’observateur pour prévenir plusieurs objets et l’état pour changer le comportement d’un objet.
Mots-clés
| Mot | Ce que ça veut dire | Exemple |
|---|---|---|
| Observateur | un objet qui veut être prévenu d’un changement | |
| Sujet | l’objet observé, qui garde la liste de ses observateurs | |
| S’abonner | se mettre sur la liste des objets prévenus | |
| Notifier | appeler chaque observateur quand il y a un changement | |
| État | la situation d’un objet qui décide de ce qu’il fait | lecture, pause |
| Patron état | un objet par état, auquel on confie le comportement |
Les cellules que les étudiants remplissent restent vides dans cet aperçu.
Sans observateur, le stock connaît tout
Si la méthode qui change le stock appelle elle-même le journal, puis l’alerte, puis l’affichage, elle connaît tous ces objets. Chaque nouvel intéressé oblige à la rouvrir.
On retourne la dépendance : c’est le stock qui garde une liste, et chaque intéressé s’y inscrit.
Un stock qui prévient ses abonnés
class Stock:
def __init__(self, quantite):
self.quantite = quantite
self.observateurs = []
def abonner(self, observateur):
self.observateurs.append(observateur)
def changer(self, quantite):
self.quantite = quantite
for observateur in self.observateurs:
observateur.prevenir(quantite)
class Journal:
def prevenir(self, quantite):
print("Journal :", quantite)
stock = Stock(20)
stock.abonner(Journal())
stock.changer(12)
stock.changer(3)
Deux observateurs aux rôles différents
Le fichier des commandes ajoute un second observateur, Alerte. Le journal note chaque quantité. L’alerte ne parle que sous 5.
Le stock les traite de la même façon : il appelle prevenir sur chacun, dans l’ordre des abonnements.
Créer un dossier pour les essais
mkdir -p essaisAffichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.
Écrire le stock et ses observateurs
printf 'class Stock:\n def __init__(self, quantite):\n self.quantite = quantite\n self.observateurs = []\n\n def abonner(self, observateur):\n self.observateurs.append(observateur)\n\n def changer(self, quantite):\n self.quantite = quantite\n for observateur in self.observateurs:\n observateur.prevenir(quantite)\n\n\nclass Journal:\n def prevenir(self, quantite):\n print("Journal :", quantite)\n\n\nclass Alerte:\n def prevenir(self, quantite):\n if quantite < 5:\n print("Stock bas :", quantite)\n' > essais/stock.pyAffichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.
Changer le stock deux fois
PYTHONPATH=essais python3 -c "from stock import Stock, Journal, Alerte; s = Stock(20); s.abonner(Journal()); s.abonner(Alerte()); s.changer(12); s.changer(3)"Affichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.
Le patron état : un objet par situation
Un lecteur de musique réagit au bouton de façon différente selon qu’il est à l’arrêt, en lecture ou en pause. Une suite de if sur un texte devient vite illisible.
Avec le patron état, chaque situation est une classe. Le lecteur confie le bouton à l’objet état courant, qui rend l’état suivant.
Un lecteur et ses trois états
class Arret:
nom = "arret"
def appuyer(self):
return Lecture()
class Lecture:
nom = "lecture"
def appuyer(self):
return Pause()
class Pause:
nom = "pause"
def appuyer(self):
return Lecture()
class Lecteur:
def __init__(self):
self.etat = Arret()
def appuyer(self):
self.etat = self.etat.appuyer()
lecteur = Lecteur()
lecteur.appuyer()
print(lecteur.etat.nom)
Écrire le lecteur et ses états
printf 'class Arret:\n nom = "arret"\n\n def appuyer(self):\n return Lecture()\n\n\nclass Lecture:\n nom = "lecture"\n\n def appuyer(self):\n return Pause()\n\n\nclass Pause:\n nom = "pause"\n\n def appuyer(self):\n return Lecture()\n\n\nclass Lecteur:\n def __init__(self):\n self.etat = Arret()\n\n def appuyer(self):\n self.etat = self.etat.appuyer()\n' > essais/lecteur.pyAffichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.
Appuyer trois fois sur le bouton
PYTHONPATH=essais python3 -c "from lecteur import Lecteur; l = Lecteur(); l.appuyer(); print(l.etat.nom); l.appuyer(); print(l.etat.nom); l.appuyer(); print(l.etat.nom)"Affichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.
Erreur fréquente
Attention
Beaucoup confondent l’observateur et la stratégie. La stratégie est choisie une fois et on lui demande un calcul. L’observateur reçoit des nouvelles à chaque changement et ne rend rien. Retirez un observateur qui n’a plus besoin d’être prévenu.
Écrire le sujet d’un observateur
Dans le devoir, vous écrivez la classe Stock. abonner(observateur) et desabonner(observateur) gèrent la liste.
changer(quantite) range la nouvelle quantité, puis appelle prevenir(quantite) sur chaque abonné, dans l’ordre. Les tests lui donnent leurs propres observateurs.