begin process at 2012 02 11 02:07:42
  Trouver un code source :
 
dans
 
Accueil > 

Tutoriels

 > 

Tutoriaux

 > DESIGN PATTERN OBSERVER

DESIGN PATTERN OBSERVER


 Information sur le tutoriel

Note :
Aucune note


 Description

Design Pattern Observer C#
Présentation du pattern Observer très connu avec un petit exemple simple à l'appui pour illustrer le concept.

Tutorial

Design Pattern Observer

Introduction

Un design pattern est un concept destiné à résoudre des problèmes récurrents suivant un certain paradigme (dans notre cas, celui de la programmation orienté objet). Il existe des dizaines de patterns différents qui simplifient grandement la vie du développeur.

Pour ce premier tutorial sur les designs patterns, j’ai choisi d’expliquer et de proposer une implémentation via un exemple du design pattern Observer. Ce pattern est utilisé pour observer l’état d’un objet dans un programme.

Voici à quoi ressemble ce pattern d’un point de vue UML



Implémentation

Rien ne vaut un petit exemple simple et pratique pour mettre en œuvre ce pattern. Imaginons que nous avons des articles en vente et que nous avons également des clients qui vont être intéressés par ces objets.
Si le prix d’un article mis en vente varie, il faudrait que les clients puissent être informés de ce changement.

Voici l’implémentation que je propose pour ce problème.
Pour commencer, l’observer, c'est-à-dire l’interface qui va nous mettre à disposition une méthode update.

using System;

namespace DPObserver
{

/// ------------------------------------------------------------------
/// <summary>
/// Represents the observer object.
/// </summary>
/// ------------------------------------------------------------------
public interface IClient
{

/// ---------------------------------------------------------------
/// <summary>
/// The article has been updated.
/// </summary>
/// ---------------------------------------------------------------
void Update(DefaultArticle defaultArticle);

}
}

Ensuite, notre classe Client qui va implémenter cette interface

namespace DPObserver
{

/// ------------------------------------------------------------------
/// <summary>
/// Represents the concrete observer.
/// </summary>
/// ------------------------------------------------------------------
public class Client : IClient
{
private string _name = null;

/// ---------------------------------------------------------------
/// <summary>
/// Create a new client.
/// </summary>
/// ---------------------------------------------------------------
public Client(string name)
{
this._name = name;
}

/// ---------------------------------------------------------------
/// <summary>
/// Get the client's name.
/// </summary>
/// ---------------------------------------------------------------
public string Name
{
get { return this._name; }
}

/// ---------------------------------------------------------------
/// <summary>
///
/// </summary>
/// ---------------------------------------------------------------
public void Update(DefaultArticle defaultArticle)
{
Console.WriteLine("Client {0} notified! Article {1}: new price = {2}", this._name, defaultArticle.Name, defaultArticle.Price);
}
}
}

La partie ‘Observer’ est donc très simple. Passons maintenant à l’implémentation du sujet. On commence par la classe abstraite qui nous définies les méthodes ‘d’abonnements’.

namespace DPObserver
{

/// ------------------------------------------------------------------
/// <summary>
/// Represents the subject object.
/// </summary>
/// ------------------------------------------------------------------
public abstract class DefaultArticle
{
private List<IClient> _observers = new List<IClient>();
protected float _price = 0f;
protected string _name = String.Empty;

/// ---------------------------------------------------------------
/// <summary>
/// Get or set the price.
/// </summary>
/// ---------------------------------------------------------------
public float Price
{
get { return this._price; }
set
{
this._price = value;
this.NotifyObservers();
}
}

/// ---------------------------------------------------------------
/// <summary>
/// Get the name.
/// </summary>
/// ---------------------------------------------------------------
public string Name
{
get { return this._name; }
}

/// ---------------------------------------------------------------
/// <summary>
/// Attach an observer.
/// </summary>
/// <param name="client">The observer to attach.</param>
/// ---------------------------------------------------------------
public void Attach(IClient client)
{
if (!this._observers.Contains(client)) this._observers.Add(client);
}


/// ---------------------------------------------------------------
/// <summary>
/// Detach an observer.
/// </summary>
/// <param name="client">The observer to dettach.</param>
/// ---------------------------------------------------------------
public void Detach(IClient client)
{
if (this._observers.Contains(client)) this._observers.Remove(client);
}

/// ---------------------------------------------------------------
/// <summary>
/// Notify all observers.
/// </summary>
/// ---------------------------------------------------------------
protected void NotifyObservers()
{
foreach (IClient obs in this._observers) obs.Update(this);
}
}

}

Puis, pour terminé, notre Article qui est l’implémentation concrète

namespace DPObserver
{
/// ------------------------------------------------------------------
/// <summary>
/// Represents the concrete subject.
/// </summary>
/// ------------------------------------------------------------------
public class Article : DefaultArticle
{

/// ---------------------------------------------------------------
/// <summary>
/// Create a new article.
/// </summary>
/// <param name="name"></param>
/// <param name="price"></param>
/// ---------------------------------------------------------------
public Article(string name)
{
this._name = name;
}
}
}

Et voilà, on a déjà mis en place le mécanisme pour que notre client soit notifié des changements. Ecrivons quelques lignes des tests :

namespace DPObserver
{
public class Program
{

public static void Main(string[] args)
{

var article1 = new Article("CodeS-SourceS") { Price = 1234.50f };
var article2 = new Article("Livre C#") { Price = 432.10f };
var article3 = new Article("Abonnement MSDN") { Price = 12345.60f };
var article4 = new Article("Camion poubelle") { Price = 123.4f };
var article5 = new Article("Sifflet") { Price = 12f };

var client1 = new Client("Bidou");
var client2 = new Client("Nix");

article1.Attach(client1);
article1.Attach(client2);
article1.Price = 88.5f;
article1.Detach(client1);
article1.Price = 73.3f;

article2.Attach(client1);
article2.Attach(client2);
article2.Price = 400f;

article3.Attach(client1);
article3.Price = 14000f;

article5.Attach(client2);
article5.Price = 12f;

}
}
}

Les résultats sont bien ceux attendus :

Conclusions

Ce pattern est assez utilisé et pas très compliqué à mettre en place. Dans la réalité, le cas sera certainement plus complexe. On peut par exemple tout à fait imaginer que l’objet qui joue le rôle d’observateur soit en même temps le sujet d’un autre observerateur, et qu’on ait ainsi une chaîne entre les différents objets.


 Historique

06 décembre 2007 21:11:18 :
mise en page
06 décembre 2007 21:14:28 :
Mise en page

Commentaires

Commentaire de billou_13 le 07/12/2007 09:43:41

Merci Bidou pour la source, expliqué et instructive.

Certes, elle est simple, mais personne n'y pense quand le cas se présente. Alors que ce pattern pourrait résoudre bien des problèmes.

A garder en tête pour le prochain développement,

Commentaire de ecosmose le 27/01/2008 17:19:32

Perso, un second code plus générique aurait été le bienvenu pour conserver de l'aspect conceptuel mais je vous note 10 pour saluer l'excellente initiative et la mise en application dans le code présent du design pattern Observer...

Très utile lors de l'emploi de Factory pour associer les ojets observeur des observés...

Utile dans l'emploi de Webservice (Serveur) pour gérer les abonnements aux ressources (sur la BD) utilisées par des observeurs (clients). Il permet d'intégrer avec souplesse et legereté les notifications de modifications des ressources paratagées en Base par plusieurs clients déconnectés ;-)...L'architecture décrite est , d'après mes recherches, seulement intégrée au Framework 3.0 et supérieure au sein de WCF...

Un grand Merci et Bravo donc ;-)

Commentaire de Yoteco le 15/02/2008 12:19:28

Salut,

Merci Dubuis ! Par contre tu as oublié la classe conteneur quand tu retourne l'objet (this) au observeur !

Je t'ai mis 10 parce que t'es un bon type.

Commentaire de madebyhisto le 30/07/2008 21:29:53

Salut,

   C'est une bonne initiative, cependant ce type d'exemple est la référence "théorique" d'un problème pratique. Un cas de figure qui aurait voulu la peine d'être fait est un exemple de type window application et qui possède des logs fichiers ainsi qu'un log d'affichage direct (Cross-Threading). Sinon cette exemple sert à rien mis à part ceux qui développe des applications console en 2008...Désolé pour la note difficile mais je ne crois pas que de démontrer un principe sur le module auquel ce principe ne sert pas est une bonne démonstration.

Cordialement,

Commentaire de Bidou le 31/07/2008 11:22:15 administrateur CS

Je ne pense pas que tu aies raison. On trouve sur le net pleins d'articles concernant ce pattern, voir des portions de codes, mais assez rarement un exemple complet. Ok, cet exemple-ci ne sera d'aucune utilité pour personne en l'état, mais il se veut volontairement simple pour qu'il soit compris de tous.

L'exemple que tu donnes (logs fichiers) est un exemple parmis probablement des centaines de milliers possible. Le tien en est à mon avis justement un très mauvais exemple pour implémenter un pattern qui a pour but d'expliquer le fonctionnement car il fait intervenir d'autres concepts, comme le cross threading (il y a un très bon tutos cela dit en passant si tu ne sais pas ce que c'est et que tu cherchais des infos à ce sujet (http://www.csharpfr.com/tutoriaux/CSHARP-OPERATIONS-CROSS-THREADS-UTILISATION-DELEGATIONS-SYNCHRONES-ASYNCHRONES_174.aspx)).

Enfin et pour conclure, tu te trouves ici dans la section "tutos": aucune source ne peut être jointe avec le texte, il serait donc totalement stupide de proposer une application complète en mettant 40 classes et 5000 lignes de codes sur une seule page (voir dans la section code pour les sources complètes).

Pour ta note, tu as dû faire une fausse manip' car elle n'est visiblement pas passée...

Commentaire de Malkuth le 18/08/2008 15:21:25

Si je ne m'abuse, les évenements .Net sont entiérement gérer par ce pattern au travers de classes plus ou moins visible les fameux "event" qui représente le "Notify()" du sujet et les "subj.evt += ..." et "subj.evt -= ..." qui représente les "attach()" et "detach()" du sujet le "Udpdate()" de l'observer est lui représenter par le déléguer passer e avec l'"attach()" => subj.evt += obs.UpdateOnEvt.

Pas clair? béh... pas mieux :s

 Ajouter un commentaire




Nos sponsors


Sondage...

CalendriCode

Février 2012
LMMJVSD
  12345
6789101112
13141516171819
20212223242526
272829    

Consulter la suite du CalendriCode

Photothèque

 
Développement réalisé par Nicolas SOREL (Nix) avec l'aide de : Cyril DURAND et Emmanuel (EBArtSoft), Merci à Vincent pour ses précieux conseils.
CodeS-SourceS.com© Toute reproduction même partielle est interdite sauf accord écrit du Webmaster
CodeS-SourceS.com© est une marque déposée tous droits réservés

Google Coop CodeS-SourceS Google Coop CodeS-SourceS
Temps d'éxécution de la page : 0,078 sec (3)

Nous contacter | Annoncer sur CodeS-SourceS | Mentions légales