IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

C++ Discussion :

[design pattern] mediateur


Sujet :

C++

  1. #1
    Membre actif Avatar de ronan99999
    Inscrit en
    Juillet 2003
    Messages
    279
    Détails du profil
    Informations personnelles :
    Âge : 44

    Informations forums :
    Inscription : Juillet 2003
    Messages : 279
    Points : 299
    Points
    299
    Par défaut [design pattern] mediateur
    Bonjour,
    je cherche une solution sur le pattern médiateur.

    Voici ci-dessous ce que je voudrais réaliser:

    J'ai une hiérarchie de classe paramètre qui vont typé mais notification.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    class PrmBase { /*...*/ };
    class PrmDeriv1 : public PrmBase { /*...*/ };
    class PrmDeriv2 : public PrmBase { /*...*/ };
    class PrmDeriv3 : public PrmBase { /*...*/ };
    class PrmDeriv4 : public PrmBase { /*...*/ };
    Et une classe médiateur:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
     
    class Mediator {
    /*...*/
    public:
       static void Notify(Colleague *subject, const PrmBase& prm)
      {
        /*...*/
        Colleague *observer = GetObserver(subject);
        if(observer) observer->Process(prm);
       /*...*/
      }
    };
    Et une classe collégue:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     
    class Colleague {
    /*...*/
    public:
     virtual void Process(const PrmBase& prm) {}
     virtual void Process(const PrmDeriv1 & prm) {}
     virtual void Process(const PrmDeriv2 & prm) {}
     virtual void Process(const PrmDeriv3 & prm) {}
     virtual void Process(const PrmDeriv4 & prm) {}
    };
    Et que... comme par magie dans la classe mediateur dans notify le process de l'observer soit le bon.

    //Dans une classe collègue concrète

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
    class ColleagueConcrete : public Colleague {
    /*...*/
       void Update()
      {
         /*...*/
         Mediator::Notify(this, PrmDeriv2);
         //Notify call virtual void Process(const PrmDeriv2 & prm)
      }
    Avez-vous une idée de comment réaliser ceci.

    Merci.
    Si tu ne te plantes pas, comment veux tu pousser?

  2. #2
    Rédacteur
    Avatar de 3DArchi
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    7 634
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 7 634
    Points : 13 017
    Points
    13 017
    Par défaut
    Salut,
    C'est le problème du visiteur et de l'absence de double/multi-dipatch en C++. Tu peux trouver un peu d'info ici.

  3. #3
    Membre actif Avatar de ronan99999
    Inscrit en
    Juillet 2003
    Messages
    279
    Détails du profil
    Informations personnelles :
    Âge : 44

    Informations forums :
    Inscription : Juillet 2003
    Messages : 279
    Points : 299
    Points
    299
    Par défaut
    Sinon J'avais penser à un pas élégant comme:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
     
    class Colleague 
    {
    /*...*/
     void Process(const PrmBase &prm)
    {
      {
       PrmDeriv1* pPrm = dynamic_cast<PrmDeriv1*>&prm;
       if(pPrm) process(*pPrm);
      }
      {
       PrmDeriv2* pPrm = dynamic_cast<PrmDeriv2*>&prm;
       if(pPrm) process(*pPrm);
      }
      /*...*/
    }
    };
    Mais ça ne me satisfait pas ou alors de templétiser "Notify" et "process" par type de paramétre.

    ou le plus simple de ne pas faire mon indirection sur le type mais sur un enum plus un type...

    Oups je viens de voir ta réponse MERCI.
    Le dynamique cast me gène un peut mais si y'a pas mieux je pense que je l'utiliserais.
    Si tu ne te plantes pas, comment veux tu pousser?

  4. #4
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 614
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 614
    Points : 30 626
    Points
    30 626
    Par défaut
    Salut,

    De manière générale, je dirais que, déjà, la classe Colleague ne devrait connaitre, au mieux, que la classe de base PrmBase et utiliser le polymorphisme des objets dérivés de PrmBase afin que sa fonction membre process puisse n'avoir qu'une seule implémentation.

    Ainsi, PrmBase ne serait qu'une interface (abstraite ) proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    class PrmBase
    {
        public:
            virtual void doSomething() = 0;
    };
    avec les classes dérivées qui se contenteraient d'implémenter la méthode en question.

    Et la classe Colleague pourrait se contenter d'un
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    class Colleague 
    {
        public:
            // la fonction n'a même pas besoin d'être virtuelle :D
            void process(PrmBase const & ref) 
            {
                ref.doSomething();
            }
    };
    Si, maintenant, process doit adapter son comportement en fonction du type réel de PrmBase, tu pourrais envisager d'appliquer une chaine de responsabilité sous une forme proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    class ResponsibleBase
    {
        public:
            ResponsibleBase():next_(NULL){}
            virtual void handle(PrmBase const &) const =0;
            //l'implémentation devrait etre dans un *.cpp ;)
            virtual ~ResponsibleBas() 
            {
            }
            void add(ResponsibleBase * b)
            {
                if(next)
                    next_->add(b);
                else
                    next_=b;
            }
        protected:
           ResponsibleBase* next_;
    };
    class ResponsiblePrmDerived1 : public ResponsibleBase
    {
        public:
            ResponsibleDerived1() //serait idéalement dans un *.cpp
            {
                next_=new ResponsibleDerived2;
            }
            virtual void handle(PrmBase const &) const;
    };
    class ResponsiblePrmDerived2 : public ResponsibleBase
    {
        public:
            ResponsibleDerived2() //serait idéalement dans un *.cpp
            {
                next_=new ResponsibleDerived3;
            }
            virtual void handle(PrmBase const &) const;
    };
    class ResponsiblePrmDerived3 : public ResponsibleBase
    {
        public:
            ResponsibleDerived3() //serait idéalement dans un *.cpp
            {
                next_=new ResponsibleDerived4;
            }
            virtual void handle(PrmBase const &) const;
    };
    class ResponsiblePrmDerived4 : public ResponsibleBase
    {
        public:
            virtual void handle(PrmBase const &) const;
    };
    (chaque classe dérivée de ResponsibleBase créant l'instance de la classe dérivée suivante... sauf la dernière)
    dont l'implémentation des méthode handle auraient le squelette suivant:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    void ResponsiblePrmDerived1::handle(PrmBase const & b) const
    {
        /* dynamic_cast lance une exception std::bad_cast s'il échoue
         * lorsqu'il est utilisé sur une référence
         *
         * profitons en :D
         */
        try
        {
            /* pour les autres, nous utiliseront les type PrmDeriv2, et autres :D
             */
            PrmDeriv1 const & = dynamic_cast<PrmDeriv1 const &> (b);
            /* si nous arrivons ici, b est bien du typpe PrmDeriv1 */
            /* ... ce qui doit être fait */
        }
        catch(std::bad_cast & e)
        {
            /* ce n'est pas le bon type, on passe au suivant */
            /* mais il n'y en a peut etre pas... */
            if(!next_)
                throw UnexpectedType(); // une exception "de ton cru" pouvant
                                        // etre récupérée si ca foire ;)
            next_->handle(b);
        }
    }
    Tu aurais alors, quelque part, mais de manière à ce la variable firstHandler soit accessible à ta classe colleague (une variable statique, dans la fonction process ou un pointeur statique en accessibilité protected dans la classe ) un code créant la chaine de responsabilité sous la forme de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    ResponsiblePrmDerived1  firstHandler;
    et ta fonction process deviendrait
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    void Colleague::process(PrmBase const & b)
    {
        firstHandler.handle(b);
    }
    Maintenant, ce n'est que "moyen moyen" du point de vue de la vitesse d'exécution, dans le sens où cela risque de tester tous les types dérivés de PrmBase si, d'aventure, le type réel de b est... le dernier de la liste

    Mais, de cette manière, tu limite les dépendances au grand maximum:

    • Ton médiateur ne doit connaitre que
      1. la classe Colleague
      2. la déclaration de PrmBase
    • la classe Colleague ne doit connaitre que
      1. la classe ResponsibleBase
      2. la déclaration de PrmBase
    • chaque classe dérivée de ResponsibleBase ne doit connaitre que
      1. la classe dérivée de PrmBase qu'elle doit gérer
      2. la classe dériviée de ResponsibleBase suivante
    Si, un jour, tu dois ajouter une classe dérivée de PrmBase supplémentaire, c'est "relativement" vite fait:
    1. tu crées la classe en question
    2. tu crée une nouvelle responsabilité dérivée de ResponsibleBase (en n'incluant le fichier d'en-tête de la classe dérivée de PrmBase que dans le *.cpp) (tu n'oublie pas de redéfinir la fonction handle )
    3. tu inclus le fichier d'en-tête de cette classe dérivée de ResponsibleBase uniquement dans le fichier qui contient l'implémentation du constructeur de la "dernière" responsabilité
    4. tu modifie le constructeur de la "dernière" responsabilité pour qu'il construise la nouvelle
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  5. #5
    Membre actif Avatar de ronan99999
    Inscrit en
    Juillet 2003
    Messages
    279
    Détails du profil
    Informations personnelles :
    Âge : 44

    Informations forums :
    Inscription : Juillet 2003
    Messages : 279
    Points : 299
    Points
    299
    Par défaut
    Merci.

    Ça m'a l'air complet.

    Je regarde ça plus en détail.
    Si tu ne te plantes pas, comment veux tu pousser?

Discussions similaires

  1. Réponses: 4
    Dernier message: 24/02/2009, 12h06
  2. [VS.NET] Les design pattern et DOTNET
    Par Nycos62 dans le forum Visual Studio
    Réponses: 4
    Dernier message: 22/10/2004, 14h44
  3. [Observateur] Précisions sur le design pattern Observer [UML]
    Par joquetino dans le forum Design Patterns
    Réponses: 2
    Dernier message: 07/10/2004, 22h35
  4. Les Designs Patterns Entreprise
    Par boulon dans le forum Design Patterns
    Réponses: 4
    Dernier message: 01/09/2004, 19h16
  5. [Design Patterns] Architecture 3 tiers
    Par HPJ dans le forum Design Patterns
    Réponses: 1
    Dernier message: 29/07/2003, 11h49

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo