Le patron de conception observateur/observable est utilisé en programmation pour envoyer un signal à des modules qui jouent le rôle d'observateur. En cas de notification, les observateurs effectuent alors l'action adéquate en fonction des informations qui parviennent depuis les modules qu'ils observent (les "observables").
La notion d'observateur/observable permet de coupler des modules de façon à réduire les dépendances aux seuls phénomènes observés.
Prenons comme exemple une classe qui produit des signaux (données observables), visualisée à travers des panneaux (observateurs) d'une interface graphique. On souhaite que la mise à jour d'un signal modifie le panneau qui l'affiche. Afin d'éviter l'utilisation de thread ou encore d'inclure la notion de panneau dans les signaux il suffit d'utiliser le patron de conception observateur/observable.
Le principe est que chaque classe observable contient une liste d'observateurs, ainsi à l'aide d'une méthode de notification l'ensemble des observateurs est prévenu. La classe observée hérite de "Observable" qui gère la liste des observateurs. La classe Observateur est quant à elle purement abstraite, la fonction de mise à jour ne pouvant être définie que par une classe spécialisée.
L'exemple ci-dessous montre comment utiliser l'API du langage Java qui propose des interfaces et des objets abstraits liées à ce patron de conception.
class Signal extends Observable { void setData(byte[] lbData){ setChanged(); // Positionne son indicateur de changement notifyObservers(); // (1) notification } }
class JPanelSignal extends JPanel implements Observer { void init(Signal lSigAObserver) { lSigAObserver.addObserver(this); // (2) ajout d'observateur } void update(Observable observable, Object objectConcerne) { repaint(); // (3) traitement de l'observation } }
Dans cet exemple en C++, on veut afficher les événements qui se produisent dans une classe Exemple.
#include#include #include
using namespace std; class IObserver { public: virtual void update(string data) = 0; }; class Observable { private: list<IObserver*> list_observers; public: void notify(string data) { // Notifier tout les observers for (list<IObserver*>::iterator it = this->list_observers.begin(); it != this->list_observers.end(); ++it) (*it)->update(data); } void addObserver(IObserver* observer) { // Ajouter un observer a la liste this->list_observers.push_back(observer); } }; class Display : public IObserver { void update(string data) { cout << "Evenement: " << data << endl; } }; class Exemple : public Observable { public: void message(string message) { // Lancer un evenement lors de la reception d'un message this->notify(message); } }; int main() { Display display; Exemple exemple; // On veut que "Display" soit prevenu a chaque reception d'un message dans "Exemple" exemple.addObserver(&display); // On envoie un message a Exemple exemple.message("reception d'un message"); // Sera affiché par Display return (0); }
Tout comme Iterateur, Observateur est implémenté en C# par l'intermédiaire du mot clé event. La syntaxe a été simplifiée pour l'abonnement ou appel d'une méthode sur levée d'un événement. Un événement possède une signature : le type de la méthode que doit lever l'évènement. Dans cet exemple c'est EventHandler.
event EventHandler observable;
La signature du type délégué EventHandler est "void (object emetteur, EventArgs argument)".
using System; ///un observateur class Kevin { public void Reception(object sender, EventArgs e) { Console.WriteLine("Kevin a reçu: {1} de: {0}", sender.ToString(), e.ToString()); Console.ReadKey(); } } class Program { ///la liste d'abonnés static event EventHandler observable; static void Main() { var kevin = new Kevin(); // enregistrement de Kevin dans la liste d'abonnés observable += new EventHandler(kevin.Reception); // si la liste n'est pas vide, prévenir les abonnés if (observable != null) observable(AppDomain.CurrentDomain, new BiereEventArgs() { Bouteilles = 2 }); } } ///que du fonctionnel class BiereEventArgs : EventArgs { public uint Bouteilles; public override string ToString() { return string.Format("{0} bouteille{1}", (Bouteilles > 0) ? Bouteilles.ToString() : "Plus de", (Bouteilles > 1) ? "s" : string.Empty); } }
La classe observable implémente le patron de conception observateur.
Rails fournit un générateur pour ce patron de conception. Il s'utilise comme ceci:
/usr/local/projetsawd# script/generate observer essai exists app/models/ exists test/unit/ create app/models/essai_observer.rb create test/unit/essai_observer_test.rb