Design Patterns : Observer

Après le design patterns Singleton et Strategy voici venu l’heure de se pencher sur le Design Patterns Observer.

Contexte

Une société d’autoroute souhaite mettre en place un système de notifications pour informer les utilisateurs des conditions de circulation. Voici le besoin que le client écrit :

Notre société souhaite que nos partenaires puissent être informés en temps réel des conditions de circulation sur le notre réseau. Il est impératif que nos partenaires soient au courant de tout changement de condition de circulation.

Analyse

Tout d’abord nous allons avoir un objet que nous nommerons InfoReseauAutoroute, c’est lui qui centralisera les informations sur les conditions de circulation du réseaux.

Nous avons dans ce contexte un sujet (Le réseau routier) et des observateurs (Les partenaires du client). Pour implémenter le design patterns Observer nous allons créer deux interfaces, ISujet et IObservateur. La société d’autoroute n’a aucune idée de comment seront traitées les informations par les clients, elle se contente de fournir ces informations et d’implémenter un système de notifications pour avertir d’un changement.

A titre de comparaison on pourrait prendre comme exemple le principe d’abonnement à un magazine. On s’abonne au magazine et, chaque mois, on reçoit ce magazine, le magazine étant dans ce cas là le sujet et nous les observateurs. C’est donc au sujet de connaître qui sont ses observateurs.

Il faut  une méthode pour pouvoir ajouter (Attach() ou Subscribe() la plupart du temps) un observateur au sujet mais également une méthode pour enlever un observateur (Detach() ou Unsubscribe()). La méthode de notification ce content quant à elle d’appeler la méthode de mise à jour de l’observateur.

Voici le code de nos deux interfaces :

public interface ISujet
    {
        void Attach(IObservateur obs);
        void Detach(IObservateur obs);
        void Notify();
    }

public interface IObservateur
    {
        void Update();
    }

Maintenant nous allons mettre en place la classe InfoReseauAutoroute qui implémentera l’interface ISujet. Notre client nous a donné les informations que doit contenir notre classe :

  • AutorouteSudTraffic
  • AutorouteNordTraffic

Ces deux informations seront les propriétés de notre classe dont voici le code, à chaque fois que leur valeur change nous appelons la méthode de notification des observateurs.

public class InfoReseauAutoroute : ISujet
    {
        // IMPLEMENTATION DU DESIGN PATTERN OBSERVER - Partie Sujet
        private IList _listeObservateurs = new List();

        public void Attach(IObservateur obs)
        {
            this._listeObservateurs.Add(obs);
        }

        public void Detach(IObservateur obs)
        {
            IObservateur obsAEnlever = this._listeObservateurs[this._listeObservateurs.IndexOf(obs)];

            if (obsAEnlever != null)
                this._listeObservateurs.Remove(obsAEnlever);
        }

        public void Notify()
        {
            this._listeObservateurs.ToList().ForEach(el =>
            {
                el.Update();
            });
        }

        // IMPLEMENTATION DE LA LOGIQUE DE INFORESEAUAUTORROUTE
        private string _autorouteSudTraffic;
        public string AutorouteSudTraffic
        {
            get { return _autorouteSudTraffic; }
            set
            {
                _autorouteSudTraffic = value;
                this.Notify();
            }
        }

        private string _autorouteNordTraffic;
        public string AutorouteNordTraffic
        {
            get { return _autorouteNordTraffic; }
            set
            {
                _autorouteNordTraffic = value;
                this.Notify();
            }
        }
    }

Maintenant disons qu’il existe deux sites web partenaires, un pour les autoroutes du nord et l’autre pour les autoroutes du sud. Ces deux classes doivent bien entendu avoir une référence vers le sujet pour avoir accès aux informations de trafic lors d’une notification.

Voici l’implémentation de l’interface IObservateur pour nos deux Sites web :

 public class SiteWebTrafficNord : IObservateur
    {
        private InfoReseauAutoroute _operateurAutoroute;

        public SiteWebTrafficNord(InfoReseauAutoroute operateurAutoroute)
        {
            this._operateurAutoroute = operateurAutoroute;
            // On s'abonne aux infos de l'opérateur d'autoroute
            operateurAutoroute.Attach(this);
        }

        public void Update()
        {
            Console.WriteLine("L'autoroute du Nord, info trafic : " + this._operateurAutoroute.AutorouteNordTraffic);
        }
    }

 public class SiteWebTrafficSud : IObservateur
    {
        private InfoReseauAutoroute _operateurAutoroute;

        public SiteWebTrafficSud(InfoReseauAutoroute operateurAutoroute)
        {
            this._operateurAutoroute = operateurAutoroute;
            operateurAutoroute.Attach(this);
        }

        public void Update()
        {
            Console.WriteLine("L'autoroute du sud en direct : " + this._operateurAutoroute.AutorouteSudTraffic);
        }
    }

Plus qu’a tester tout ça maintenant dans notre méthode main :

static void Main(string[] args)
        {
            InfoReseauAutoroute operateurAutoroute = new InfoReseauAutoroute();
            operateurAutoroute.AutorouteNordTraffic = "Normal";
            operateurAutoroute.AutorouteSudTraffic = "Normal";

            SiteWebTrafficNord siteWebNord = new SiteWebTrafficNord(operateurAutoroute);
            SiteWebTrafficSud siteWebSud = new SiteWebTrafficSud(operateurAutoroute);

            // Changement de trafic sur l'autoroute Nord
            operateurAutoroute.AutorouteNordTraffic = "Ralentissements de 10 Km";

            // Changement de trafic sur l'autoroute Sud
            operateurAutoroute.AutorouteSudTraffic = "Bouchon sur 25Km";

            Console.ReadLine();
        }

Et voilà nos deux partenaires seront toujours au courant des dernières évolutions du trafic !

Un peu plus…

Il est possible de transmettre toutes les informations de trafic directement via la méthode Notify(). C’est une notification Push car nous “poussons” l’information vers les destinataires. Cependant qu’est-ce que le site web d’info trafic des autoroutes du Sud peut bien avoir à faire des infos trafic du Nord ? Rien. Du coup, dans notre cas, il est préférable de déléguer la récupération de ces informations à nos classes Observateurs, c’est un Pull. Ceci à également l’avantage de pouvoir ajouter des informations sans avoir à changer la signature de Notify().

A noter que d’un point de vue strictement personnel il me semble préférable d’implémenter ISujet dans une classe abstraite dont hérite InfoReseauAutoroute afin de différencier la mécanique interne du design patterns Observer (Attach, Detach et Notify) et la logique métier introduite dans InfoReseauAutoroute.

Pour information il existe depuis la sortie du Framework .Net 4.0 deux interfaces permettant d’implémenter plus facilement le pattern Observer : IObserver<T> et IObservable<T>.

Définition du Pattern Observer:

Defines a one-to-many dependency between objects so that when one object changes state, all of its dependents are notified and updated automatically

Diagramme de classe :

Diagramme de classe Observer

Leave a Reply