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 :

Heritage Multiples _ Difficulté à utiliser une structure en diamant


Sujet :

C++

  1. #1
    Membre confirmé
    Homme Profil pro
    Chargé d'affaire
    Inscrit en
    Avril 2014
    Messages
    105
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Chargé d'affaire

    Informations forums :
    Inscription : Avril 2014
    Messages : 105
    Par défaut Heritage Multiples _ Difficulté à utiliser une structure en diamant
    Bonjour tout le monde,

    Je travaille actuellement sur un projet où je dois réaliser des héritages multiples. Bien qu'utilisant la bibliothèque QT, je ne pense pas que mon problème se limite à celle-ci. Je vais quand même l'utiliser pour illustrer mon problème ici.

    Nom : HeritageDiamant.PNG
Affichages : 525
Taille : 14,0 Ko

    Dans mon monde, comme illustré ci dessus, je crée une une classe de base : le moteur.

    Je crée aussi une classe Advanced Moteur qui hérite de la classe moteur et de la classe QObject, pour pouvoir beneficier des fonctions de la classe QOject (comme vous le devinez ici, des signaux et des slots)

    A présent je considère un moteur, Rolls-Roys, qui lui communique avec mon pc avec un protocole Série. Du coup, je me dit que ça serait bien que mon moteur Rolls-Royce puisse dériver de la classe QTSerial, qui gère le protocole série, chez QT. Jusqu'à là rien ne semble bien folichon et ça devrait fonctionner.

    Là où ca se complique, c'est que si l'on regarde de plus prêt, comme j'ai essayé tant bien que mal, d'illustrer ci dessous, QTSerial, on se rendra compte que celle-ci hérite indirectement de QObject (comme toutes les classes QT) : et là, comme vous pouvez vous en douter, si je prends la fonction deleteLater, il y a ambiguïté : si je fais
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    Rolls_Roys->deleteLater()
    (fonction qui est dans qobject.h), alors le copilo ne saura pas quelle voie prendre pour aller la chercher... ce qui est normal.

    Nom : HeritageDiamant2.png
Affichages : 430
Taille : 18,1 Ko

    Alors, pour lever certaines ambiguités, j'ai essayé de virtualiser les héritages, après avoir lu http://loic-joly.developpez.com/arti...tage-multiple/. Idéalement, il faudrait que je fasse, d'après ce tutoriel la chose suivante :

    Nom : HeritageDiamant3.png
Affichages : 426
Taille : 19,1 Ko

    Or modifier QIODevice.h est à proscrire, comme vous pouvez bien l'imaginer.

    Du coup, j'ai essayé la chose illustrée ci dessous, sans succès :

    Nom : HeritageDiamant4.png
Affichages : 433
Taille : 18,9 Ko

    Il y a t-il une façon de faire cet héritage virtuel dans mon cas?

    J'ai trouvé une solution pas très jolie, je trouve, pour résoudre mon problème : c'est de surcharger les fonctions que j'utilise. Par exemple :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    void Rolls_Royce::deleteLater()
    {
    QObject::deleteLater();
    }
    Et là l’ambiguïté est levée.

    Qu'en pensez-vous? Est-ce la seule solution que je puisse utiliser?

  2. #2
    Rédacteur/Modérateur


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

    Informations professionnelles :
    Activité : Network game programmer

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

    au lieu de réimplémener la fonction, l'écrire de cette façon : using QObject::deleteLater; dans le header devrait faire l'affaire
    Sinon se passer de l'héritage pour faire de la composition (le serialDevice en particulier imo).
    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 Expert
    Avatar de prgasp77
    Homme Profil pro
    Ingénieur en systèmes embarqués
    Inscrit en
    Juin 2004
    Messages
    1 306
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 38
    Localisation : France, Eure (Haute Normandie)

    Informations professionnelles :
    Activité : Ingénieur en systèmes embarqués
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Juin 2004
    Messages : 1 306
    Par défaut
    Bonjour,

    je vais essayer d'apporter une solution qui est bien éloignée de ce que tu as imaginé jusqu'à présent : ne pas faire hériter Rolls_Roys de SerialDevice. Pour appuyer ma suggestion, deux points.

    1. De l'abus de l'héritage

    L'héritage a été considéré pendant plusieurs années comme une panacée : la solution à tous les problèmes d'architecture logicielle. À voir tous les design patterns populaires (observer, MVC, singleton, visiteur, ...), on voit bien que la plupart sont appuyés sur des notions d'héritage. <troll>La faute un peu à Java</troll>. Mais attention : l'héritage est le lien le plus fort qui peut exister entre deux classes ; faire hériter S de T afin que S puisse utiliser des compétences de T est un abus.

    Citation Envoyé par moi
    J'enfonce un clou, je ne suis pas un marteau, j'utilise un marteau.

    2. Comment bien architecturer ses classes : SOLID

    Avec le temps, des règle de bonne conduite si l'on peut dire se sont imposées. je pense notamment aux règles SOLID, acronyme dont le L désigne le Liskov substitution principle :

    Citation Envoyé par Liskov
    Substitutability is a principle in object-oriented programming stating that, in a computer program, if S is a subtype of T, then objects of type T may be replaced with objects of type S (i.e. an object of type T may be substituted with any object of a subtype S) without altering any of the desirable properties of T (correctness, task performed, etc.).
    c'est à dire que partout où un T est attendu, un S peut être fourni. Je réponds donc à ta question par une autre question :

    Y a-t-il du sens à ce/est-ce correct/... que partout où un SerialDevice ou un QObject est attendu, un Rolls_Roys soit fourni ?
    3. Ma suggestion à ton problème d'architecture

    La composition. La composition est une bonne alternative assez fréquente à l'héritage. Tout simplement.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    class Rolls_Roys : public Advanced_Motor
    {
        SerialDevice _serial;
    public:
        // ...
    }

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 635
    Par défaut
    Salut,

    Je ne peux qu'être d'accord avec prgaspp77: une rolls royce n'est pas un moteur, c'est une voiture. Et ce n'est pas d'avantage un SerialDevice:

    Une rolls royce, ca contient ("est composée" d') un moteur (et d'un SerialDevice), qui lui permettent de fournir certains services que l'on est en droit d'attendre de sa part

    Si tu as une relation d'héritage à envisager pour une rolls royce (et encore * ), ce serait rolls royce qui hérite de voiture --> (voiture qui hérite de véhicule, à voir si cela a de l'intérêt pour toi )--> véhicule (ou voiture) qui hérite de Q_OBJECT.

    (*)Je dit "et encore", car, a priori, il n'y a aucune différence de comportement entre une rolls royce et une... fiat uno. Il y a bien des différences entre ces deux types de véhicules, mais ce sont des différences de valeurs (par exemple: au niveau du prix, de la cylindrée ou du poids). Mais les comportements (allumer le moteur, accélérer, tourner, ralentir, s'arrêter, ...) restent exactement les mêmes bien qu'ils s'adaptent aux différentes valeurs dont ils disposent.

    Or, l'héritage est -- normalement -- prévu pour permettre l'adaptation des comportements, et non l'adaptation de valeurs. Les comportements d'une rolls royce étant en tout point ceux que l'on peut observer pour n'importe quelle autre voiture, nous n'avons donc, a priori, aucune raison de "perdre notre temps" à créer une classe Rolls_Royce qui hérite de Voiture. Car, si on entre dans cette logique, on voudra créer des classes Fiat, Renault, Audi, Toyota et que sais-je qui n'apporteront en réalité rien de plus que... la classe voiture d'origine
    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
    Expert éminent
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 395
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 395
    Par défaut
    @koala01: Là je me vois obligé de te contredire, awawawa a dit explicitement qu'il s'agissait d'un moteur Rolls-Royce et non d'une voiture.
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

  6. #6
    Expert éminent
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 635
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 635
    Par défaut
    Citation Envoyé par Médinoc Voir le message
    @koala01: Là je me vois obligé de te contredire, awawawa a dit explicitement qu'il s'agissait d'un moteur Rolls-Royce et non d'une voiture.
    Oups, au temps pour moi.

    Mais le raisonnement reste le même : si l'on parle du moteur de rolls royce, on peut éventuellement (*) envisager de faire hériter une classe de moteur, mais cela n'a pas énormément de sens du fait que tous les moteurs réagissent sensiblement de la même manière.

    (*) Je sais: on essayera sans doute de faire valoir qu'il y a une différence de qualité entre un moteur de trabant et un moteur de rolls royce, ou qu'il n'y a aucun point commun entre la puissance générée par un moteur de ferrari et celle générée par un moteur de fiat uno. Et je devrai avouer que celui qui me fera ce genre de réflexion a raison "dans le fond".

    Mais, dans la forme, ils auront quand même toujours tord: qu'un moteur casse après 100 000 km ou un million, qu'il utilise trois cylindres pour propulser péniblement les 800 kg de la voiture, ou qu'il profite d'un V12 pour ne pas sentir les deux tonnes et demie de la voiture, le comportement du moteur ne change absolument pas. Et il n'y a donc absolument aucune raison de créer une classe spécifique "uniquement" pour être en mesure de spécifier des valeurs particulières.

    L'idéal, au niveau du moteur, serait donc d'avoir une fonction (bool canPlug() )qui nous indiquerait si on peut y connecter un appareil susceptible de communiquer avec un SerialDevice et d'une fonction plugDevice(SomeTypeConnectedToSerialDevice *) qui nous permettrait de le connecter s'il y a moyen de le faire.

    Le truc, c'est qu'il faut bien le reconnaître, il y a toute la panoplie de cas où l'on ne peut pas vraiment se permettre de modifier l'existant. Et, par conséquent, tu n'as peut-être pas la possibilité de modifier ta classe Moteur ou ta classe Advanced_Moteur (au passage: le franglais, c'est moyen moyen, mais bon...) de manière à y rajouter ces deux fonctions.

    Dans cette éventualité, il peut s'avérer utile de créer une classe (mettons : PluggableMotor) qui hérite de Advanced_Moteur, qui agrège un SerialDevice comme membre de la classe et qui expose les fonction canPlug et plugDevice pour nous permettre d'y connecter le device de "récupération" (ou à tout le moins, la fonction plugDevice).

    Mais, comme il me semble évident que de plus en plus de moteurs disposent d'un système similaire, ce serait sans doute un tord de contraindre se type à représenter un moteur issu d'une firme particulière. Ne serait-ce que parce que tu pourras difficilement prétendre qu'un moteur d'audi (qui dispose lui aussi de ce SerialDevice) EST un moteur ... de rolls royce.

    En résumé:
    1. L'héritage doit être utilisé le moins souvent possible. L'agrégation est souvent une meilleure solution;
    2. Les types que tu crées doivent continuer à représenter des abstractions (un moteur, un moteur "évolué", un moteur "auquel on peut connecter quelque chose"), et non un élément "concret" (exemple : un moteur de rolls royce)
    3. L'héritage ne sert pas à modifier "certaines valeurs", il sert à adapter les comportements
    4. une spécialisation (exemple: spécialiser moteur en moteur de rolls royce) n'est jamais une bonne idée car ca te bloque dans tes possibilités d'évolution
    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

  7. #7
    Membre éprouvé
    Profil pro
    Inscrit en
    Novembre 2004
    Messages
    2 766
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2004
    Messages : 2 766
    Par défaut
    Totalement d'accord avec prgasp77.

    @koala01 : Les concurrents de Rolls-Royce en terme de moteur sont plutôt Safran, Pratt & Withney, ou General Electric.

  8. #8
    Membre éclairé
    Avatar de VivienD
    Homme Profil pro
    Développeur logiciel
    Inscrit en
    Octobre 2009
    Messages
    523
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 35
    Localisation : Allemagne

    Informations professionnelles :
    Activité : Développeur logiciel
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Octobre 2009
    Messages : 523
    Par défaut
    Bonjour,

    En réalité, la classe QObject ainsi que toutes les classes qui en héritent, directement ou indirectement, ne peuvent pas être héritée de manière virtuelle. En effet, dans la documentation dédiée au moc, on peut lire noir sur blanc:
    Virtual inheritance with QObject is not supported.
    Pour contourner le problème, je te conseille, moi aussi, la composition.

  9. #9
    Membre confirmé
    Homme Profil pro
    Chargé d'affaire
    Inscrit en
    Avril 2014
    Messages
    105
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Chargé d'affaire

    Informations forums :
    Inscription : Avril 2014
    Messages : 105
    Par défaut
    Salut tout le monde!

    Tout d'abord, je tiens à vous remercier pour vos multiples réponses et à m'excuser pour ma réponse tardive.

    Je vais donc essayer de répondre chronologiquement à chacun d'entre vous.

    @Bousk : effectivement ta solution marche dans mon cas, je te dis donc merci!

    @prgasp77 : alors figure toi que j'ai bien pensé à ta méthode. J'ai très longuement réfléchi à faire soit un double héritage, soit un objet qui agrège le qserialport. Honnêtement lors de la phase de vie "utilisation" du soft, je n'y voyais pas de différence, parce-que dans mon cas, je vais juste considérer le moteur rolls-royce comme étant un moteur avec les fonctions d'un moteur. Ta solution semblait alors pour moi plus pertinente. Maintenant, regardons de plus haut la classe rolls_royce et décomposons la en différentes phases de vie (utilisation, création, test, support...). Supposons que je veuille non pas utiliser la classe rolls_royce dans un phase de vie "utilisation" mais dans une phase de vie de "test". Que je crée une fonction qui test les fonctions de communication d'un périphérique en port série (où meme generallement d'un port de communication qulconque : dans ce cas re héritage)... Ben en faisant ça, je pourrai faire un au lieu d'un
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    rolls.royce.getSerialPort().testComm()
    , surchargeant éventuellement la fonction en fonction du moteur (ben il peut y avoir certaines options propres au moteur, qui rajoutent des fonctions dans le protocole de communication que je puisse vouloir tester... Voilà pourquoi à l'époque j'avais privilégié cette architecture de double héritage. A cause des différents cycle de vie que ma classe va traverser...

    @koala01 : Je ne suis pas tout à fait d'accord avec toi. Si Rolls_Royce et Fiat n'ont pas normalisé leur trames de la même façon, alors la fonction de chez Fiat et de chez Rolls_Royce n'auront rigoureusement rien à voir (bien que j’accélère, j’enverrai des trames rigoureusement différentes aux moteurs avec mes ports com). D'ailleurs, il se peut même que fiat communique en Bluetooth (je dis n'importe quoi mais c'est pour illustrer). Donc au niveau de la programmation à proprement parler de la fonction accélère() , le code sera complétement différent d'un moteur à l'autre (bien que je te l'accorde la finalité des fonctions seront identiques).

    @oodini : oui certes...

    @VivenD : gracias y merci

  10. #10
    Expert éminent
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 635
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 635
    Par défaut
    Citation Envoyé par awawawa Voir le message
    @koala01 : Je ne suis pas tout à fait d'accord avec toi. Si Rolls_Royce et Fiat n'ont pas normalisé leur trames de la même façon, alors la fonction de chez Fiat et de chez Rolls_Royce n'auront rigoureusement rien à voir (bien que j’accélère, j’enverrai des trames rigoureusement différentes aux moteurs avec mes ports com). D'ailleurs, il se peut même que fiat communique en Bluetooth (je dis n'importe quoi mais c'est pour illustrer). Donc au niveau de la programmation à proprement parler de la fonction accélère() , le code sera complétement différent d'un moteur à l'autre (bien que je te l'accorde la finalité des fonctions seront identiques).
    Mais ca, on (ou du moins le moteur) s'en fout de comment les trames seront transmises, ou même si le système qui viendra se connecter sera en mesure les comprendre.

    C'est au système de communication (ton SerialDevice) de veiller à ce qu'il transmette ses trames soient transmises "selon les specs", et c'est au système qui viendra s'y connecter de s'assurer qu'il respecte les mêmes specs de com que le SerialDevice auquel il sera connecté.

    Le moteur, il n'a rien à voir dans l'histoire, à partir du moment où ... il permet à un système de se connecter au SerialDevice
    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

  11. #11
    Membre confirmé
    Homme Profil pro
    Chargé d'affaire
    Inscrit en
    Avril 2014
    Messages
    105
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Chargé d'affaire

    Informations forums :
    Inscription : Avril 2014
    Messages : 105
    Par défaut
    Ok,

    Je prends notes de vos remarques

    Merci beaucoup pour vos commentaires!

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

Discussions similaires

  1. utiliser une structure dans une bibliothèque
    Par michel4_1 dans le forum C
    Réponses: 7
    Dernier message: 15/05/2012, 13h03
  2. Réponses: 2
    Dernier message: 10/11/2009, 08h43
  3. Utiliser une structure comme argument d'une fonction
    Par guilermo dans le forum Débuter
    Réponses: 7
    Dernier message: 15/10/2009, 10h05
  4. [XSLT] Comment utiliser une structure de type if-then-ELSE ?
    Par ribrok dans le forum XSL/XSLT/XPATH
    Réponses: 2
    Dernier message: 27/11/2006, 12h39
  5. [C#]Pourquoi utiliser une structure plutôt qu'une classe?
    Par egoom dans le forum Windows Forms
    Réponses: 2
    Dernier message: 30/10/2006, 09h49

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