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 :

[Conception] Hiérarchie de classes et maintenance


Sujet :

C++

  1. #1
    Membre du Club
    Profil pro
    Inscrit en
    Novembre 2006
    Messages
    103
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2006
    Messages : 103
    Points : 68
    Points
    68
    Par défaut [Conception] Hiérarchie de classes et maintenance
    Bonjour à tous,

    J'ai un petit souci de conception. Pour faire simple, j'ai une classe mère d'objets géométriques GeometricShape, de laquelle dérivent plusieurs classes RectangleShape, SquareShape, ParallelogramShape ...
    Par ailleurs, j'ai une classe Profil et une classe Voisinage. Or, je voudrais que mes objets géométriques possèdent ces propriétés (avoir un ou des Profil, et / ou un voisinage). Si je fais ça par des dérivations, je vais avoir un paquet de classes, et je ne pense pas que ce soit une bonne solution. Je vais par exemple me retrouver avec RectangleShapeWithProfil, RectangleShapeWithNeighborhood, RectangleShapeWithProfilAndNeighborhood ... A la limite, c'est acceptable lorsque l'on a qu'une seule classe. Mais, je dois faire de même pour chaque sous classe de GeometricShape, et à chaque fois que je rajoute une "propriété", je dois me retaper un paquet de dérivations ...
    Je trouve que c'est très lourd, et je me dis qu'il doit y avoir une façon propre de faire ça, autrement que par des dérivations. Seulement, je ne sais pas comment faire.

    Pourriez vous m'aider ?

    D'avance merci

  2. #2
    Membre averti
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Mai 2006
    Messages
    366
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : France, Paris (Île de France)

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

    Informations forums :
    Inscription : Mai 2006
    Messages : 366
    Points : 444
    Points
    444
    Par défaut
    Salut,

    Tu devrais regarder du côté du Design Pattern Decorator, il est parfaitement adapté à ton besoin

  3. #3
    Membre du Club
    Profil pro
    Inscrit en
    Novembre 2006
    Messages
    103
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2006
    Messages : 103
    Points : 68
    Points
    68
    Par défaut
    Citation Envoyé par bolhrak Voir le message
    Tu devrais regarder du côté du Design Pattern Decorator, il est parfaitement adapté à ton besoin
    OK, merci, ça va me faire un peu de lecture ... Je suis pas sûr de bien comprendre et je viendrais peut être t'embêter à nouveau si j'ai besoin de précisions.

    ++

  4. #4
    Membre averti
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Mai 2006
    Messages
    366
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : France, Paris (Île de France)

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

    Informations forums :
    Inscription : Mai 2006
    Messages : 366
    Points : 444
    Points
    444
    Par défaut
    Vais anticiper alors ^^

    En gros le but de ce DP est de pouvoir enrichir les fonctionnalités d'une classe sans en dériver. Ca permet notamment d'ajouter les fonctionnalités d'une autre famille de classes à la première famille sans faire exploser le nombre de classes.

    Dans ton cas :

    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
     
    class GeometricShape {
     
        public:
     
            virtual void operation1() =0;
            virtual void operation2() =0;
     
    };
     
    class GeometricShapeDecorator : public GeometricShape {
     
        public:
     
            GeometricShapeDecorator(GeometricShape* shape): p_shape(shape)
            {assert(p_shape!=0);}
     
            //Comportement par defaut : on délègue à l'élément wrappé
            virtual void operation1() {p_shape->operation1();}
            virtual void operation2() {p_shape->operation2();}
     
        protected:
     
            GeometricShape* p_shape;
     
    };
     
    class GeometricShapeWithProfil : public GeometricShape {
     
        public:
     
            //Constructeur à mettre
     
            //comportement redéfini
            virtual void operation1() {
                //Traitements spécifiques
                p_shape->operation1();
                //Traitements spécifiques
            };
     
    };
     
    //Utilisation :
     
    GeometricShape* p = new GeometricShapeWithProfil(new Rectangle());
    Bon il manque les destructeurs et deux trois petites choses mais globalement l'idée est là.

    Tu peux également ajouter de nouvelles méthodes dans tes Decorator, mais ça implique que tu conserves le type exact de ceux-ci lorsque tu les manipules.

  5. #5
    Membre du Club
    Profil pro
    Inscrit en
    Novembre 2006
    Messages
    103
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2006
    Messages : 103
    Points : 68
    Points
    68
    Par défaut
    Merci, je vais regarder ça avec attention.
    En revanche, j'ai une petite question. N'est il pas possible d'arriver au même résultat en utilisant des nested templates comme par exemple dans la librairie de graphes de boost ?

  6. #6
    Membre averti
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Mai 2006
    Messages
    366
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : France, Paris (Île de France)

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

    Informations forums :
    Inscription : Mai 2006
    Messages : 366
    Points : 444
    Points
    444
    Par défaut
    Si, il y a moyen de le faire également par template, je ne sais pas comment est implémentée boost.graph donc je sais pas trop si c'est le même principe.

  7. #7
    Membre du Club
    Profil pro
    Inscrit en
    Novembre 2006
    Messages
    103
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2006
    Messages : 103
    Points : 68
    Points
    68
    Par défaut
    Citation Envoyé par bolhrak Voir le message
    Si, il y a moyen de le faire également par template
    Tu pourrais me donner un petit exemple ?
    Pour les graphes de boost, tu peux attacher des propriétés à tes noeuds ou à tes arêtes de cette façon (piqué dans la doc !) :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    template <class PropertyTag, class T, class NextProperty = no_property> struct property;
    Grâce au paramètre NextProperty, tu peux emboîter tes templates et ajouter autant de propriétés que tu veux :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    typedef property<capacity_t, int> Cap;
    typedef property<flow_t, int, Cap> EdgeProperty;

  8. #8
    Membre averti
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Mai 2006
    Messages
    366
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : France, Paris (Île de France)

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

    Informations forums :
    Inscription : Mai 2006
    Messages : 366
    Points : 444
    Points
    444
    Par défaut
    Ok,

    Donc le truc auquel je pensais y ressemble un peu (enfin j'imagine, je n'ai pas le détail d'aasociation valeur des propriétés/noeud ou arrête interne à boost, bien que je soupçonne un petit héritage des familles), la différence est que tu n'aurais pas à définir tes types de propriétés puisque tu les connais déjà.

    Une autre différence est que lors de cette association, TOUS les noeuds (ou arrêtes selon les propriétés que tu définis) possèdent ces propriétés, seules les valeurs changent. Or toi tu sembles vouloir que différentes instances d'une même "classe" (plutôt hiérarchie de classes en fait) puissent posséder ou non ces propriétés au sein d'une même appli.

    En gros si ces propriétés sont un paramétrage statique, les templates sont appropriés. Si tu dois pouvoir utiliser plusieurs instances de cette hiérarchie de classes, certaines avec ces propriétés, d'autres sans, et les traiter de manière uniforme, le Decorator est plus adapté.

    Pour l'utilisation de template, plusieurs possibilités en fonction de ce que tu souhaites. Si tes classes de voisinage et profil contiennent des propriétés qui doivent enrichir l'interface de tes classes, une programmation par policy est envisageable :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    template <class Profil, class Voisinage>
    class GeometricShape : public Profil, public Voisinage {
    //...
    };
    Mais dans ce cas tu n'as plus une hiérarchie de GeometricShape, mais une famille de hiérarchies, tu ne pourras pas stocker et traiter uniformément une forme avec profil et une forme sans. Si ces classes contiennent uniquement des propriétés/fonctions utilisées par les GeometricShape, utilise plutôt la composition. Mais la contrainte précédente reste vraie.

  9. #9
    Membre du Club
    Profil pro
    Inscrit en
    Novembre 2006
    Messages
    103
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2006
    Messages : 103
    Points : 68
    Points
    68
    Par défaut
    Bon, si je reviens sur l'idée du Decorator, comme tu me l'as suggéré dès le début, et si j'ai bien compris, ça devrait donner un truc 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
    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
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    class AbstractGeometricShape
    {
    public:
       virtual ~AbstractGeometricShape() { }
       virtual void Draw() = 0;
       virtual void Rotate() = 0;
       virtual void Translate() = 0;
    };
     
    class GeometricShape : public AbstractGeometricShape
    {
    public:
       ~GeometricShape() { }
       /*virtual*/ void Draw(); // implemente dans un cpp ...
       /*virtual*/ void Rotate(); // implemente dans un cpp ...
       /*virtual*/ void Translate(); // implemente dans un cpp ...
    };
     
    class RectangleShape : public GeometricShape
    {
    public:
       RectangleShape() { }
       ~RectangleShape() { }
       // Des methodes ...
       /*virtual*/ void Draw(); // implemente dans un cpp ...
       // ...
    };
     
    class GeometricShapeDecorator : public AbstractGeometricShape
    {
    public:
       GeometricShapeDecorator( AbstractGeometricShape* inner )
       {
          m_wrappee = inner;
       }
       ~GeometricShapeDecorator()
       {
          delete m_wrappee;
       }
       /*virtual*/ void Draw(); // implemente dans un cpp ...
       /*virtual*/ void Rotate(); // implemente dans un cpp ...
       /*virtual*/ void Translate(); // implemente dans un cpp ...
    private:
       AbstractGeometricShape*  m_wrappee;
    };
     
    class GeometricShapeWithProfils : public GeometricShapeDecorator
    {
    public:
       GeometricShapeWithProfil( AbstractGeometricShape* core ) : GeometricShapeDecorator( core ) { }
       ~GeometricShapeWithProfil() { }
       /*virtual*/ void Draw()
       {
          GeometricShapeDecorator::Draw();
          std::vector<Profil>::iterator itb = m_profil.begin(), ite = m_profil.end();
          for (;itb!=ite;itb++)
             (*itb).Draw();
       }
    private:
       std::vector<Profil> m_profil; // Profil est une classe définie et implémentee ailleurs ...
    };
     
    class GeometricShapeWithNeighboor : public GeometricShapeDecorator
    {
    public:
       GeometricShapeWithNeighboor( AbstractGeometricShape* core ) : GeometricShapeDecorator( core ) { }
       ~GeometricShapeWithNeighboor() { }
       /*virtual*/ void Draw()
       {
          GeometricShapeDecorator::Draw();
          std::vector<Neighboor>::iterator itb = m_neighboor.begin(), ite = m_neighboor.end();
          for (;itb!=ite;itb++)
             (*itb).Draw();
       }
    private:
       std::vector<Neighboor> m_neighboor; // Neighboor est une classe définie et implémentee ailleurs ...
    };
    Est ce que je fais n'importe quoi ou c'est bien ça ?
    Si je veux un objet qui a à la fois un Profil et un Neighboor, je fais comment ? Je déclare un GeometricShapeWithProfilAndNeighboor que je dérive de GeometricShapeDecorator ?

    PS : faut il "virtualiser" les méthodes ?
    PPS : j'utilise les smart pointeurs de boost, ça facilite le travaill de désallocation et de la responsabilité des pointeurs. Je ne dois pas m'en préoccuper ?

  10. #10
    Membre averti
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Mai 2006
    Messages
    366
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : France, Paris (Île de France)

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

    Informations forums :
    Inscription : Mai 2006
    Messages : 366
    Points : 444
    Points
    444
    Par défaut
    Si tes classes Profil et Neighbor ont une méthode Draw, c'est bien ça.

    Si tu veux combiner Une GeometricShape avec profil et neighboorhood, il te suffit simplement de décorer deux fois ta GeometricShape : une fois avec GeometricShapeWithProfil, puis avec GeometricShapeWIthNeighboor :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
     
    GeometricShapeWithNeighboor rectangleWithProfilAndNeighboor = new GeometricShapeWithNeighboor(new GeometricShapeWithProfil(new RectangleShape));
    C'est là tout l'avantage du décorateur : tu n'est pas obligé de dériver de nouvelles classes pour combiner des classes déjà existantes, il suffit de les "empiler".

    En ce qui concerne la virtualisation, oui c'est nécessaire étant donné que tu vas manipuler différents types via leur type de base. Donc polymorphisme obligatoire. Maintenant le fait de déclarer tes méthodes "virtual" dans la classe de base les rend virtuelles pour toute ta hiérarchie de classe. Mais perso je préfère remettre le mot clé virtual à chaque fois, je trouve cela plus lisible.

    Pour l'utilisation des smart_pointer de boost, je n'en vois pas dans le code que tu m'as présenté. Maintenant si tu les utilises, effectivement il n'y a plus de raison de s'occuper des désallocations mémoires à condition de respecter les guide lines fournies par boost, et de bien gérer les distinctions shared_ptr/weak_ptr si tu as des cycles.

  11. #11
    Membre du Club
    Profil pro
    Inscrit en
    Novembre 2006
    Messages
    103
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2006
    Messages : 103
    Points : 68
    Points
    68
    Par défaut
    OK, c'est parfait, je te remercie.

    Mes Profil et Neighboor ont effectivement des méthodes Draw() et de transformations.

    Pour la virtualisation, savais pas que la dérivation les déclarait comme telle. Tu as raison, plus lisible de les mettre.

    Pour les shared_ptr et les weak_ptr, je les ais pas mis dans le code (meme pas essayé de le compiler, je l'ai fait dans le bloc notes ...) pour que ce soit plus lisible.

    Il ne me reste plus qu'à interfacer le tout avec ma factory et ça devrait bien tourner ... Possible que je revienne ...

  12. #12
    Membre averti
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Mai 2006
    Messages
    366
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : France, Paris (Île de France)

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

    Informations forums :
    Inscription : Mai 2006
    Messages : 366
    Points : 444
    Points
    444
    Par défaut
    Citation Envoyé par olivier1978 Voir le message
    Pour la virtualisation, savais pas que la dérivation les déclarait comme telle. Tu as raison, plus lisible de les mettre.
    Attention ce n'est pas vraiment le fait d'utiliser l'héritage, c'est le fait de les déclarer comme virtuelles dans la classe de base.

    Dans le code 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
     
     
    class B {
     
    public:
     
    int getValue() {...}
     
    };
     
    class A : public B {
     
    public:
     
    int getValue() {...}
     
    };
    La méthode getValue n'est en aucun cas virtuelle, celle de la classe A masque celle de la classe B (un truc à éviter au maximum).

  13. #13
    Membre du Club
    Profil pro
    Inscrit en
    Novembre 2006
    Messages
    103
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2006
    Messages : 103
    Points : 68
    Points
    68
    Par défaut
    Citation Envoyé par bolhrak Voir le message
    Attention ce n'est pas vraiment le fait d'utiliser l'héritage, c'est le fait de les déclarer comme virtuelles dans la classe de base.
    Je me suis mal exprimé, mais c'est ce que je voulais dire.

  14. #14
    Membre du Club
    Profil pro
    Inscrit en
    Novembre 2006
    Messages
    103
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2006
    Messages : 103
    Points : 68
    Points
    68
    Par défaut
    bolhrak, un grand merci à toi ! J'ai testé le tout ce soir, il reste encore quelques petites choses à implanter, mais globalement, ça a l'air de plutôt bien fonctionner.

    En revanche, une question que je me pose. Mon code commence à devenir assez complexe, même si l'utilisation de divers patterns (fabrique, singleton et décorateur ...) rend la programation plus facile. En gros, avec tous ces héritages, ce polymorphisme de tous les côtés, des dynamic_cast, des boost::dynamic_pointer_cast, les performances en terme de rapidité sont elles plombées ?

    Je le passe en résolu ...

  15. #15
    Membre averti
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Mai 2006
    Messages
    366
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : France, Paris (Île de France)

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

    Informations forums :
    Inscription : Mai 2006
    Messages : 366
    Points : 444
    Points
    444
    Par défaut
    Citation Envoyé par olivier1978 Voir le message
    bolhrak, un grand merci à toi ! J'ai testé le tout ce soir, il reste encore quelques petites choses à implanter, mais globalement, ça a l'air de plutôt bien fonctionner.
    Content d'avoir pu t'aider

    En revanche, une question que je me pose. Mon code commence à devenir assez complexe, même si l'utilisation de divers patterns (fabrique, singleton et décorateur ...) rend la programation plus facile. En gros, avec tous ces héritages, ce polymorphisme de tous les côtés, des dynamic_cast, des boost::dynamic_pointer_cast, les performances en terme de rapidité sont elles plombées ?
    En ce qui concerne l'héritage et le polymorphisme, un appel de fonction membre virtuelle coûte une indirection de plus qu'un appel de fonction membre classique. Autrement dit quasiment rien, ce n'est pas ça qui va dégrader les perfs d'une appli.

    Les dynamic_cast sont, quant à eux, un peu plus coûteux, il faut éviter, dans la mesure du possible, de les utiliser, mais il y a des cas de figure où on n'a pas le choix ; par ailleurs le fait de vouloir les éviter ne relève pas en premier lieu d'un souci de performance, mais d'un souci de design. Avant de voir leur impact sur les perfs, il y a pas mal d'autre choses à regarder au niveau du code pour l'optimiser.

  16. #16
    Expert éminent sénior
    Avatar de Luc Hermitte
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2003
    Messages
    5 275
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Août 2003
    Messages : 5 275
    Points : 10 985
    Points
    10 985
    Par défaut
    Comme je l'ai déjà dit dans le passé : Si tu as besoin de décider dynamiquement d'un traitement, les fonctions virtuelles ont un coût parfaitement acceptable (si ce n'est mieux) que les solutions alternatives non OO comme 5 if, un switch, un tableau de pointeurs de fonctions, etc.

    C'est un coût qui dans l'absolu est illusoire. Il faut le comparer avec le coût des autres solutions qui résolvent nos problèmes. Pas avec le coût d'une fonction non virtuelle qui ne résoud pas les même problèmes.
    Blog|FAQ C++|FAQ fclc++|FAQ Comeau|FAQ C++lite|FAQ BS|Bons livres sur le C++
    Les MP ne sont pas une hotline. Je ne réponds à aucune question technique par le biais de ce média. Et de toutes façons, ma BAL sur dvpz est pleine...

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

Discussions similaires

  1. Hiérarchie de class et membres spécifiques
    Par bolhrak dans le forum C++
    Réponses: 6
    Dernier message: 23/10/2006, 13h23
  2. Réponses: 2
    Dernier message: 25/08/2006, 22h18
  3. [conception] vecteur de classes
    Par r0d dans le forum C++
    Réponses: 7
    Dernier message: 28/12/2005, 12h00
  4. [CONCEPTION] vector de classes heritées
    Par A-S-H dans le forum SL & STL
    Réponses: 9
    Dernier message: 28/12/2005, 09h50
  5. Conception d'une classe parente
    Par VincentB dans le forum Langage
    Réponses: 9
    Dernier message: 24/06/2003, 17h28

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