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 :

Question d'architecture : déléguer des responsabilités


Sujet :

C++

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre éclairé Avatar de Ekinoks
    Profil pro
    Étudiant
    Inscrit en
    Novembre 2003
    Messages
    687
    Détails du profil
    Informations personnelles :
    Âge : 38
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Novembre 2003
    Messages : 687
    Par défaut Question d'architecture : déléguer des responsabilités
    Salut,

    Ca fait quelque jour que je suis confronté à un problème de conception en C++.
    Voici une simplification du problème.

    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
     
    class A {
        void afficher() {
            for (auto it = tab.begin(); it != tab.end(); ++it) 
                it->get()->afficher();
        }
        vector< shared_ptr<B> > tab;
    };
     
    class B {
        virtual void afficher() = 0;
    };
     
    class C : public B {
        virtual void afficher() { ... }
        ...
    };
     
    class D : public B {
        virtual void afficher() { ... }
        ...
    };
    On a donc une classe A qui contient des objets héritant de la class B.
    Imaginons maintenant que la méthode afficher soit complexe. Pour réduire les responsabilités de chaque classe, il serait alors naturelle de retirer cette tache de ces classes.
    Seulement voilà, je n'arrive pas à faire ça proprement : je me retrouve à utiliser des dynamic_cast

    Exemple :

    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
     
    void afficher( A &a )
    {
        for (auto it = a.getTab().begin(); it != a.getTab().end(); ++it)
        {
            C* c;
            D* d;
            if( (c = dynamic_cast<C>( it->get() )) != NULL )
            {
                ...
            } else if( (d = dynamic_cast<D>( it->get() )) != NULL )
            {
                ...
            }
        }
    }
    PS : La même problématique peut ce poser pour les attributs des classes.


    Que pensez vous de ce problème ?
    Avez vous des idées ?


    Merci pour votre aide,
    Ekinoks

  2. #2
    Rédacteur/Modérateur


    Homme Profil pro
    Network game programmer
    Inscrit en
    Juin 2010
    Messages
    7 151
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 38
    Localisation : Canada

    Informations professionnelles :
    Activité : Network game programmer

    Informations forums :
    Inscription : Juin 2010
    Messages : 7 151
    Billets dans le blog
    4
    Par défaut
    Bonjour,

    quelque chose comme ça. Qui utilise juste le polymorphisme.
    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 BDrawer {
     virtual void afficher() { cout<<"BDrawer"<<endl;}
    };
    class CDrawer : public BDrawer{
     virtual void afficher() { cout<<"CDrawer"<<endl; }
    };
    class B
    {
      virtual void init() { m_pdrawer = new BDrawer; }
      void afficher() { m_pdrawer->afficher(); }
     
      BDrawer* m_pdrawer;
    };
    class C : public B
    {
      virtual void init() { m_pdrawer = new CDrawer; }
    }
    Pensez à consulter la FAQ ou les cours et tutoriels de la section C++.
    Un peu de programmation réseau ?
    Aucune aide via MP ne sera dispensée. Merci d'utiliser les forums prévus à cet effet.

  3. #3
    Membre éclairé Avatar de Ekinoks
    Profil pro
    Étudiant
    Inscrit en
    Novembre 2003
    Messages
    687
    Détails du profil
    Informations personnelles :
    Âge : 38
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Novembre 2003
    Messages : 687
    Par défaut
    Merci pour ta réponse Bousk

    Effectivement cette solution délègue des responsabilités de manière simple.
    Par contre, le fait que la classe "B" doive contenir un objet "BDrawer" n'est-il pas gênant ?
    La classe "B" n'ayant plus la responsabilité d'affichage, peut-il contenir un objet en rapport avec l'affichage ?
    Je vois un problème à cela (peut être inévitable) : si on souhaite ajouter une fonctionnalités, on doit modifier la classe B.
    Par exemple, si je souhaite ajouter une fonctionnalité de vérification je doit ajouter un attribut "BVerif* m_pverif" dans la classe B.

    Vois tu une solution à ce problème ? ou n'es pas vraiment un problème ?

  4. #4
    Rédacteur/Modérateur


    Homme Profil pro
    Network game programmer
    Inscrit en
    Juin 2010
    Messages
    7 151
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 38
    Localisation : Canada

    Informations professionnelles :
    Activité : Network game programmer

    Informations forums :
    Inscription : Juin 2010
    Messages : 7 151
    Billets dans le blog
    4
    Par défaut
    Dans le cas où un objet a un drawer spécifique, je ne vois pas d'autre alternative.
    Dans un cas où chaque objet a le même drawer spécifique selon un autre critère (que l'objet en lui-même, ie : utiliser directX, ou opengl), tu peux imaginer quelque chose comme ça:

    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
    class B {};
    class IDrawer { virtual void draw(B*)=0; };
    class DXDrawer { virtual void draw(B*) {...}; };
    class GLDrawer { virtual void draw(B*) {...}; };
    class DrawEngine {
    void init() {
     // if use DX
     m_drawer = new DXDrawer;
     // else if use GL
     m_drawer = new GLDrawer;
     // else
     // error : no drawer ?
    }
    void Draw(B* b) { m_drawer->draw(b); }
    IDrawer* m_drawer;
    };
    Qui sera peut-être mieux dans le cas où tout le rendu se fait dans un 2° thread.
    On peut aussi imaginer une list des B de l'application à afficher en membre de DrawEngine, l'affichage se chargeant lui-même de parser cette liste, et à ce moment-là on ne fait plus aucun appel à B::draw qui n'existerait plus ! La seule action que B doit réaliser est de prévenir DrawEngine qu'il existe (et à l'opposé le prévenir quand il n'existe plus).
    Après il faut voir les spécificités : s'il faut recompiler le shader pour chaque objet, refournir les vertex etc, est-ce que ça vaut le coup ?
    Je ne suis pas allé aussi loin dans cette idée, qui m'avait semblé correct pour de la 2D avec la SDL mais j'ignore sa complexité pour la 3D open GL et les shaders.

    Mais si par la suite tu veux ajouter un BVerif, ben... il faut bien le rajouter quelque part^^
    Que ce soit sous forme de visiteur sur B, son Drawer ou en membre de B initialisé : même combat amha.
    Pensez à consulter la FAQ ou les cours et tutoriels de la section C++.
    Un peu de programmation réseau ?
    Aucune aide via MP ne sera dispensée. Merci d'utiliser les forums prévus à cet effet.

  5. #5
    Membre éclairé Avatar de Ekinoks
    Profil pro
    Étudiant
    Inscrit en
    Novembre 2003
    Messages
    687
    Détails du profil
    Informations personnelles :
    Âge : 38
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Novembre 2003
    Messages : 687
    Par défaut
    Bien vu !! le pattern visiteur correspond à ce que je veux !

    Voila ce que ça donne :
    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
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
     
    struct Visitor {
        virtual void visiteA( A * ) = 0;
        virtual void visiteC( C * ) = 0;
        virtual void visiteD( D * ) = 0;
    };
     
    struct Container {
        virtual void visite( Visitor * ) = 0;
    };
     
    struct A : public Container {
        void addB( std::shared_ptr<B> b ) {
            tab.push_back( b );
        }
     
        virtual void visite( Visitor * v ) {
            v->visiteA( this );
        }
     
        std::vector< std::shared_ptr<B> > tab;
    };
     
    struct B : public Container { };
     
    struct C : public B {
        virtual void visite( Visitor * v ) {
            v->visiteC( this );
        }
    };
     
    struct D : public B {
        virtual void visite( Visitor * v ) {
            v->visiteD( this );
        }
    };
     
    struct Afficheur : public Visitor {
            virtual void visiteA( A * a ) {
                std::cout << "afficher A : " << std::endl;
                for (auto it = a->tab.begin(); it != a->tab.end(); ++it)
                    it->get()->visite( this );
            }
     
            virtual void visiteC( C * c ) {
                std::cout << "afficher C" << std::endl;
            }
     
            virtual void visiteD( D * d ) {
                std::cout << "afficher D" << std::endl;
            }
    };
     
    int main() {
        A a;
        a.addB( std::make_shared<C>() );
        a.addB( std::make_shared<D>() );
     
        Afficheur aff;
        a.visite( &aff );
    }
    Pour ajouter une fonctionnalité, il faut juste créer une classe qui hérite de Visitor =)

    PS : D'habitude je les retrouve par moi même les design pattern mais celui ci n'est pas évident... je comprend maintenant l'utilité de les apprendre


    Encore merci pour ton aide.

  6. #6
    Responsable 2D/3D/Jeux


    Avatar de LittleWhite
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Mai 2008
    Messages
    27 128
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Mai 2008
    Messages : 27 128
    Billets dans le blog
    149
    Par défaut
    Le visiteur, c'est le même que l'observateur : http://come-david.developpez.com/tut...servateur#LVII ?
    Vous souhaitez participer à la rubrique 2D/3D/Jeux ? Contactez-moi

    Ma page sur DVP
    Mon Portfolio

    Qui connaît l'erreur, connaît la solution.

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Réponses: 3
    Dernier message: 26/12/2007, 10h34
  2. question sur le rafraichissement des données dans la base
    Par vbcasimir dans le forum Bases de données
    Réponses: 8
    Dernier message: 06/06/2005, 12h44
  3. question sur le comportement des threads
    Par rose-bonbon dans le forum CORBA
    Réponses: 4
    Dernier message: 27/10/2004, 18h00
  4. question sur le format des images ..
    Par vbcasimir dans le forum Langages de programmation
    Réponses: 7
    Dernier message: 28/08/2003, 12h08

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