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

Langages de programmation Discussion :

Héritage classes carré/rectangle


Sujet :

Langages de programmation

  1. #1
    Membre éprouvé
    Avatar de Spout
    Profil pro
    Ingénieur systèmes et réseaux
    Inscrit en
    Février 2007
    Messages
    904
    Détails du profil
    Informations personnelles :
    Âge : 39
    Localisation : France, Val d'Oise (Île de France)

    Informations professionnelles :
    Activité : Ingénieur systèmes et réseaux

    Informations forums :
    Inscription : Février 2007
    Messages : 904
    Points : 1 067
    Points
    1 067
    Par défaut Héritage classes carré/rectangle
    Bonjour à tous,

    Lorsque j'étais étudiant, les profs qui m'ont enseigné l'objet (Java) m'ont sorti cet exemple de la classe carré qui hérite de la classe rectangle.
    Aujourd'hui, je tombe sur de plus en plus de discussions à ce sujet stipulant que c'est une aberration de conception.

    Ma question est alors simple: pourquoi?
    (avec un minimum d'explications SVP )
    "L'ordinateur obéit à vos ordres, pas à vos intentions." [Anonyme]

  2. #2
    Expert confirmé

    Profil pro
    Leader Technique
    Inscrit en
    Juin 2005
    Messages
    1 756
    Détails du profil
    Informations personnelles :
    Âge : 46
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Leader Technique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juin 2005
    Messages : 1 756
    Points : 4 170
    Points
    4 170
    Par défaut
    Aujourd'hui, je tombe sur de plus en plus de discussions à ce sujet stipulant que c'est une aberration de conception.
    Quoi donc ? Le carré qui dérive du rectangle ? Tu as des exemples ?

    Parce que pour moi, toujours l'exemple qu'on prend pour expliquer qu'il ne faut pas tomber dans le piège de l'héritage d'implémentation :
    - Normalement, un carré est un rectangle particulier, donc tu dois le faire dériver du rectangle.
    - L'héritage d'implémentation consiste à dire : J'ai des classes, je factorise les attributs communs dans une classe de base, puis je la spécialise pour ajouter les attributs manquants dans les classes dérivées.
    Si t'ammènes à dire : Pour modéliser un carré, je n'ai besoin que de la longueur, ou la largeur. Donc j'ai un seul attribut.
    Pour modéliser un rectangle, j'ai besoin d'une dimension de plus, donc je spécialise le carré pour lui ajouter la longueur qui me manque.
    Tu bâtis ainsi l'héritage uniquement à partir des attributs communs que tu factorises, sans qu'il y est nécessairement de lien sémantique entre les classes.

    Pour moi, c'est l'héritage d'implémentation qui est une abérration. Si ta hiérarchie de classe ne modélise pas un lien sémantique, ton modèle finira par tomber sur des contradictions qui vont tout casser...

  3. #3
    Membre Expert

    Homme Profil pro
    Ingénieur R&D
    Inscrit en
    Juin 2003
    Messages
    4 506
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Ingénieur R&D
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2003
    Messages : 4 506
    Points : 5 724
    Points
    5 724
    Par défaut
    comme dirais zaho c'est chelou !!!! En tout cas pour moi (concepteur pas mathématicien) ce n'est pas forcément facile à affirmer ou infirmer, dans l'ambiguité je ferais une superclass quadrilatere dont je dérive rectangle et carré comme cela plus de problème de polémique
    " Dis ce que tu veux qui insulte mon honneur car mon silence sera la réponse au mesquin.
    Je ne manque pas de réponse mais : il ne convient pas aux lions de répondre aux chiens ! " [Ash-Shafi'i ]

  4. #4
    Membre éprouvé
    Avatar de Spout
    Profil pro
    Ingénieur systèmes et réseaux
    Inscrit en
    Février 2007
    Messages
    904
    Détails du profil
    Informations personnelles :
    Âge : 39
    Localisation : France, Val d'Oise (Île de France)

    Informations professionnelles :
    Activité : Ingénieur systèmes et réseaux

    Informations forums :
    Inscription : Février 2007
    Messages : 904
    Points : 1 067
    Points
    1 067
    Par défaut
    Citation Envoyé par Franck SORIANO Voir le message
    Quoi donc ? Le carré qui dérive du rectangle ?
    Exactement.
    Citation Envoyé par Franck SORIANO Voir le message
    Tu as des exemples ?
    http://www.developpez.net/forums/d18...a/#post1373282
    http://www.dotnetguru2.org/index.php...&c=1&tb=1&pb=1
    "L'ordinateur obéit à vos ordres, pas à vos intentions." [Anonyme]

  5. #5
    Expert confirmé

    Profil pro
    Leader Technique
    Inscrit en
    Juin 2005
    Messages
    1 756
    Détails du profil
    Informations personnelles :
    Âge : 46
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Leader Technique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juin 2005
    Messages : 1 756
    Points : 4 170
    Points
    4 170
    Par défaut
    Tu peux être plus précis ? je ne vois ou on parle du carré et du rectangle, et vu la longueur des posts, j'ai pas trop envie de fouiller.

    Merci, je me suis bien marré . L'auteur est contre l'héritage parce qu'il ne le maîtrise pas et montre les problèmes qu'on rencontre lorsqu'on fait n'importe quoi.
    Il ne dit pas que faire dériver le carré du rectangle est une aberration de conception.
    Regardons ses explications :

    La classe Carre ne va pas seulement hériter de propriétés dont elle n’a pas besoin, mais aussi éventuellement de méthodes qui n’ont peut-être aucun sens pour elle.
    Ben la, on n'est plus dans le cas du Rectangle et du Carré. Nous on parle des objets mathématiques. Hors un carré est un rectangle. Tout ce qui s'applique à un rectangle s'applique indifféremment à un carré.
    Dans son cas, sa classe Rectangle n'était déjà plus un rectangle. Mais un rectangle plus autre chose. Et il nous dit maintenant que ce quelque chose ne concerne pas le carré... Donc normalement dans ce cas, tu ne dérives pas du rectangle. Tu fais plutôt un coup de refactoring pour éclater la classe "Rectangle + Quelque chose", en "Rectangle" puis "Quelque chose" qui dérive de Rectangle.

    D’autres problèmes plus graves peuvent surgir. Par exemple la fonction suivante apparaît dans mon application. Elle est parfaitement légale :

    void MaFonction(Rectangle r) {…}

    Tout objet implémentant Rectangle peut être reçu en paramètre et donc en particulier un Carre.

    Supposons que le code de Mafonction() a besoin de calculer 1/(Hauteur – Largeur) . Cette opération ne pose jamais de problème pour un Rectangle.
    Faux : Rien n'interdit d'avoir un Rectangle particulier, avec Hauteur = Largeur. Ce n'est pas parce que tu as un rectangle que tu peux en déduire que Hauteur-Largeur<>0.
    Donc son code était boggué dès le départ et ne marchait déjà pas avec un rectangle.
    Le fait de faire dériver Carré de Rectangle lui a simplement permis de découvrir son bug, mais ce n'est pas la faute du carré.

    Derrière toute la suite de sa démonstration tombe à l'eau puisque le point de départ est faux.

    Donc pour en revenir à la question du départ, je ne vois toujours rien pour justifier que faire dériver Carré de rectangle serait une abérration de conception.

    ...dans l'ambiguité je ferais une superclass quadrilatere dont je dérive rectangle et carré comme cela plus de problème de polémique
    Sauf que dans ce cas, tu perds le bénéfice de l'héritage. Si tu doit faire une fonction qui doit travailler sur un rectangle : Par exemple qui calcule son aire en faisant largeur*hauteur. Si Carré dérive de rectangle, tu peux appliquer cette fonction indifféremment sur un carré et sur un rectangle. Si carré ne dérive plus de rectangle, mais seulement de Quadrillatère, tu ne peux plus l'utiliser avec un carré...

    En fait, dans le cas du carré et du rectangle, je vois qu'en même une abérration : Le carré est un rectangle. Mais une instance particulière d'un rectangle peut très bien devenir un carré. Il suffit que sa longueur soit égale à sa largeur.
    Sa signifie qu'en changeant la valeur d'un attribut de l'objet, il faudrait que l'objet change également de type...

    Ce qui revient à dire qu'on n'a pas besoin de dériver Rectangle...

  6. #6
    Expert éminent sénior
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 287
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Manche (Basse Normandie)

    Informations professionnelles :
    Activité : Architecte technique retraité
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2008
    Messages : 21 287
    Points : 36 776
    Points
    36 776
    Par défaut
    Je pense que le débat est plutôt sur les subtiles différences entre héritage (est-un) et généralisation (est une sorte de).
    -W
    Architectures post-modernes.
    Python sur DVP c'est aussi des FAQs, des cours et tutoriels

  7. #7
    Membre Expert

    Homme Profil pro
    Ingénieur R&D
    Inscrit en
    Juin 2003
    Messages
    4 506
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Ingénieur R&D
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2003
    Messages : 4 506
    Points : 5 724
    Points
    5 724
    Par défaut
    Citation Envoyé par wiztricks Voir le message
    Je pense que le débat est plutôt sur les subtiles différences entre héritage (est-un) et généralisation (est une sorte de).
    -W
    est-un ou est-une-sorte-de c'est exactement la même chose. Une banane est un fruit ou une banane est une sorte de fruit c'est du pareil au même.


    Citation Envoyé par Franck SORIANO
    Sauf que dans ce cas, tu perds le bénéfice de l'héritage.Si tu doit faire une fonction qui doit travailler sur un rectangle : Par exemple qui calcule son aire en faisant largeur*hauteur. Si Carré dérive de rectangle, tu peux appliquer cette fonction indifféremment sur un carré et sur un rectangle.Si carré ne dérive plus de rectangle, mais seulement de Quadrillatère, tu ne peux plus l'utiliser avec un carré..

    Minute, une petite minute.

    Par exemple une superclass Quadrilatere ensuite subclasser en Quad1Dimension, Quad2Dimension.....pour enfin subclasser en carré et rectangle.

    Bon à partir de là rien ne m'empêche de définir Quadrilatere comme abstract et de définir une méthode calculeAire. Carre et Rectangle dérivant de Quadrilatere (par généralisation)ils héritent bien de la méthode on peut faire carre.calculeAire() et rect.calculeAire().

    Enfin cela pourrait être une solution avec héritage, en alternative de dériver un carre d'un rectangle.

    Ce qui revient à dire qu'on n'a pas besoin de dériver Rectangle...
    Deriver d'un rectangle bof...l'agréger oui !

    je me demande si le pattern adaptateur ne ferait pas l'affaire...
    " Dis ce que tu veux qui insulte mon honneur car mon silence sera la réponse au mesquin.
    Je ne manque pas de réponse mais : il ne convient pas aux lions de répondre aux chiens ! " [Ash-Shafi'i ]

  8. #8
    Expert confirmé

    Profil pro
    Leader Technique
    Inscrit en
    Juin 2005
    Messages
    1 756
    Détails du profil
    Informations personnelles :
    Âge : 46
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Leader Technique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juin 2005
    Messages : 1 756
    Points : 4 170
    Points
    4 170
    Par défaut
    Bon à partir de là rien ne m'empêche de définir Quadrilatere comme abstract et de définir une méthode calculeAire. Carre et Rectangle dérivant de Quadrilatere (par généralisation)ils héritent bien de la méthode on peut faire carre.calculeAire() et rect.calculeAire().
    Dans ce cas, tu as deux implémentations qui vont faire la même chose. A ce niveau, autant passer par des interfaces plutôt que par héritage.
    Mais là, on change de débat...

    Deriver d'un rectangle bof...l'agréger oui !

    je me demande si le pattern adaptateur ne ferait pas l'affaire...
    Comme je le disais on change de débat.
    On est trop dans le vague pour répondre à la question. Ce genre de choix s'effectue plutôt au cas par cas.
    Mais pour moi, le cas du carré/rectangle est justement celui dans lequel j'utilise l'héritage.

    Passer par de l'aggrégation, des interfaces ou un adapteur, c'est adapté si on veut modéliser des comportements : Si on veut dire qu'un carré se comporte comme un rectangle, sans pour autant en être un.
    Mais un carré ne fait pas que se comporter comme un rectangle : c'est un rectangle. L'héritage est donc parfaitement adapté.

  9. #9
    Membre expert
    Avatar de hed62
    Homme Profil pro
    Développeur .NET
    Inscrit en
    Juillet 2007
    Messages
    2 029
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 39
    Localisation : France, Nord (Nord Pas de Calais)

    Informations professionnelles :
    Activité : Développeur .NET
    Secteur : Santé

    Informations forums :
    Inscription : Juillet 2007
    Messages : 2 029
    Points : 3 134
    Points
    3 134
    Par défaut
    Là où ca commence à se compliquer, c'est lorsqu'on se met à parler de losange. Enfin, ca se complique si on n'aime pas l'héritage multiple, puisqu'un carré "est un" losange et "est un" rectangle.

    Dans ce cas là, je préfère utiliser la composition : on donne à une instance de la classe Forme les divers "outils" qu'elle est en mesure d'avoir. Tous les outils (ainsi que les formes) répondent d'un même interface.

    A une instance de Rectangle, on fournit donc Quadrilatère et Rectangle , à une instance de Carre, on fournit Quadrilatere, Rectangle et Losange, etc...

    A charge de la classe Carre ou Rectangle de déléguer correctement.

    Bien sur, il y a surement des failles dans ce que j'ai dit, et il faudrait raffiner tout cela, mais je pense qu'il est possible de s'en sortir ainsi.
    Hervé Delannoy, Ingénieur études&développement.

    Je n'accepte pas les demandes de mise en relation MSN/yahoo sans motif.
    ------------------------------------------------------------------------
    Si , ni , ne peuvent vous aider, mais nous oui, pensez à un pti et au !
    Merci de vous relire
    ____________________________________________________________________________________
    Recherche joueurs de "Magic" sur Lille et environs.
    Donner plutôt que jeter.

  10. #10
    Membre Expert

    Homme Profil pro
    Ingénieur R&D
    Inscrit en
    Juin 2003
    Messages
    4 506
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Ingénieur R&D
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2003
    Messages : 4 506
    Points : 5 724
    Points
    5 724
    Par défaut
    Citation Envoyé par Franck SORIANO Voir le message
    Dans ce cas, tu as deux implémentations qui vont faire la même chose. A ce niveau, autant passer par des interfaces plutôt que par héritage.
    Mais là, on change de débat...
    Une seule implémentation qui est dans Quadrilatere c'est tout. Mais à vrai dire je préfére quand même la composition à l'héritage dans ce cas... Au lieu de dire un carre est un rectangle je dirais un carre à un rectangle.

    C'est vrai que comme dis hed62 cela peut vite se compliquer avec les autres formes géométriques, la délégation permet d'avoir de bonnes conceptions.
    " Dis ce que tu veux qui insulte mon honneur car mon silence sera la réponse au mesquin.
    Je ne manque pas de réponse mais : il ne convient pas aux lions de répondre aux chiens ! " [Ash-Shafi'i ]

  11. #11
    Expert confirmé

    Profil pro
    Leader Technique
    Inscrit en
    Juin 2005
    Messages
    1 756
    Détails du profil
    Informations personnelles :
    Âge : 46
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Leader Technique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juin 2005
    Messages : 1 756
    Points : 4 170
    Points
    4 170
    Par défaut
    Citation Envoyé par hegros Voir le message
    Une seule implémentation qui est dans Quadrilatere c'est tout.
    Et tu met quoi dans ton implémentation ? Tu ne peut pas faire Largeur*Hauteur... Sinon tu auras des problèmes lorsque tu voudras créer Parallelogramme.
    De plus, si tu mets tous dans Quadrilatère, Pourquoi le sous-classer ensuite en Carré et Rectangle ?

    Citation Envoyé par hed62 Voir le message
    Là où ca commence à se compliquer, c'est lorsqu'on se met à parler de losange. Enfin, ca se complique si on n'aime pas l'héritage multiple, puisqu'un carré "est un" losange et "est un" rectangle.
    C'est clair que l'héritage multiple complique tout de suite beaucoup les choses.

    Citation Envoyé par hed62 Voir le message
    Dans ce cas là, je préfère utiliser la composition : on donne à une instance de la classe Forme les divers "outils" qu'elle est en mesure d'avoir. Tous les outils (ainsi que les formes) répondent d'un même interface.

    A une instance de Rectangle, on fournit donc Quadrilatère et Rectangle , à une instance de Carre, on fournit Quadrilatere, Rectangle et Losange, etc...

    A charge de la classe Carre ou Rectangle de déléguer correctement.

    Bien sur, il y a surement des failles dans ce que j'ai dit, et il faudrait raffiner tout cela, mais je pense qu'il est possible de s'en sortir ainsi.
    Le problème avec la délégation c'est qu'on ne modélise que les comportements. Donc si c'est ton seul besoin, tu t'en sort facilement (ce qui dans la pratique est souvent le cas).
    Sauf que le lien entre un carré et un rectangle est beaucoup plus fort que "un carré c'est un truc qui se comporte comme un rectangle".
    Tu as un lien sémantique fort entre les deux que tu casses si tu travailles par déléguation. Tu ne peux plus dire : 'Un carré est un rectangle'. Tu peux seulement dire : "Un carré est un truc qui se comporte comme un rectangle".
    Si on est dans le contexte du départ, c'est à dire qu'on parle des formes géométriques, si un carré n'est plus un rectangle c'est quand même gênant...

    Bien sûr tout dépend de ce qu'on est en train de faire. Si on commence à entrer dans l'héritage multiple, ça devient vite une grosse prise de tête et il est beaucoup plus simple de faire de la composition et délégation.

  12. #12
    Membre Expert

    Homme Profil pro
    Ingénieur R&D
    Inscrit en
    Juin 2003
    Messages
    4 506
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Ingénieur R&D
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2003
    Messages : 4 506
    Points : 5 724
    Points
    5 724
    Par défaut
    Citation Envoyé par Franck SORIANO Voir le message
    Et tu met quoi dans ton implémentation ? Tu ne peut pas faire Largeur*Hauteur... Sinon tu auras des problèmes lorsque tu voudras créer Parallelogramme.
    De plus, si tu mets tous dans Quadrilatère, Pourquoi le sous-classer ensuite en Carré et Rectangle ?
    non je ne mettrais pas la méthode calculeAire au niveau de Quadrilatere mais un niveau ou quelques niveaux en dessous (peut-être dans Quad1Dimension) je ne sais pas ce n'est pas complet de plus les figures géométriques il y a plusieurs façon de voir les choses, là j'ai compris surtout qu'on cherchait une solution avec héritage...Pour l'instant je ne vois pas de voix à faire dériver un carré d'un rectangle en cherchant un peu plus on devrait pouvoir subclasser pour obtenir quelque chose de meilleur conceptuellement...
    " Dis ce que tu veux qui insulte mon honneur car mon silence sera la réponse au mesquin.
    Je ne manque pas de réponse mais : il ne convient pas aux lions de répondre aux chiens ! " [Ash-Shafi'i ]

  13. #13
    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
    LSP (Liskov Substitution Principle) est le mot clé.
    Cherche l'article de Robert C Martin sur objectmentor à ce sujet.

    EDIT: En gros: (pseudocode)
    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 Carre : substituable à Rectangle {
        Invariant: largeur == longueur;
    ...
    };
    
    void f(Rectangle [inout] r){
        const fa1 = r.aire;
        r.largeur *= 2;
        const fa2 = r.aire;
        postcondition: fa2 == 2 *fa1;
    }
    
    Carre r = ....;
    const a1 = r.aire;
    f(r);
    const a2 = r.aire;
    // a2 == 4 * a1, ou 2 * a1 ?
    Avec cet héritage,
    - soit on viole la postcondition de f si la modification de la largeur du carré entraine celle de sa longueur
    - soit on viole l'invariant du carré si modifier la largeur n'entraine pas la modification de la largeur.

    Bref, un Carre ne sera jamais entièrement substituable à un Rectangle. Mais rien ne nous empêche d'importer le code d'un Rectangle pour définir un Carré. Typiquement par délégation/composition dans tous les langages OO, ou encore par héritage privé en C++ ; ruby a un mécanisme à part pour cela si j'ai bien suivit.

    Dans l'autre sens, ce n'est pas mieux, on va retomber sur des problèmes similaires.

    La faute, c'est tous ces bouquins qui ont enseigné que l'héritage est génial car on peut enfin réutiliser du code. Déjà ce n'est pas comme si on ne le pouvait pas avant. Ensuite, dans les langages maintstream, l'héritage c'est pour "pouvoir être utilisé en place de", c'est ce fantastique outil qui permet la substituabilité.
    Dans d'autres langages, on va parler de polymorphisme de second ordre (je crois) plutôt que de LSP, mais je ne connais pas assez pour en parler correctement. Je passe la main.


    Dans le même ordre d'idée des Carrés qui ne sont pas des Rectangles, mais plus simple à appréhender pour un informaticien, AMA (*), une ListeTriee n'est pas une Liste. Soit on a une insertion qui permet d'insérer où l'on veut, soit respecte l'invariant d'ordre.

    (*) vu que l'on ne contredit pas notre intuition mathématique
    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...

  14. #14
    Expert confirmé

    Profil pro
    Leader Technique
    Inscrit en
    Juin 2005
    Messages
    1 756
    Détails du profil
    Informations personnelles :
    Âge : 46
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Leader Technique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juin 2005
    Messages : 1 756
    Points : 4 170
    Points
    4 170
    Par défaut
    Avec cet héritage,
    - soit on viole la postcondition de f si la modification de la largeur du carré entraine celle de sa longueur
    - soit on viole l'invariant du carré si modifier la largeur n'entraine pas la modification de la largeur.
    Disons surtout que l'héritage ne s'utilise pas à la légère. Il y a des règles à respecter, et l'une d'entre elle nottamment c'est qu'on ne dérive pas une classe si la modification d'un attribut doit entrainer un changement de type de l'objet (en général, c'est ce qu'on désigne par : "Faut-il créer un nouvel attribut ou dériver les classes ?").
    Appliqué à nos Carrés et Rectangles, ça donne : Si tu peux changer les dimensions de ton Rectangle et de ton Carré, le carré va devoir se transformer en Rectangle.
    Dans ce cas, tu n'as pas le droit de faire la dérivation, car Carré n'est pas une nouvelle classe, ce n'est qu'un état particulier que peut prendre Rectangle.

    La classification Carré dérive de Rectangle, Carré est un Rectangle, suppose qu"on travaille avec des objets immuables, tels que les figures géométriques au sens mathématique :
    Tu ne peux pas modifier la largeur d'un Carré, en réalité tu appliques une transformation dont le résultat est une autre figure géométrique, ce n'est plus le Carré de départ.

    Donc si tu peux modifier les dimmensions de ton Rectangle, tu n'as pas le droit de le dériver pour faire Carré. Carré n'a pas de raison d'être, ce n'est alors qu'un état particulier de Rectangle.

    La faute, c'est tous ces bouquins qui ont enseigné que l'héritage est génial car on peut enfin réutiliser du code.
    Je dirais surtout la faute, c'est tous ces bouquins et cours qui enseignent l'héritage sans parler des contraîntes qui vont avec et des règles à respecter. Ou encore, celle des étudiants qui retiennent le résultat et qui oublient qu'il y a des règles à respecter avant de pouvoir s'en servir.

    Mais je suis d'accord pour dire que l'héritage, ce n'est pas la solution miracle à tous les problèmes. Si on l'utilise à la légère, on tombe très vite sur les pièges de l'héritage d'implémentation. Malheureusement, comme tu dis la plupart des gens ne voient dans l'héritage que la possibilité de faire de l'héritage d'implémentation.
    Combien de fois on lit dans les forums : "Pour faire ta modélisation objet, c'est très simple : Tu définis toutes les classes dont tu as besoin avec les méthodes et attributs nécessaires. Puis tu factorises les attributs et méthodes communs en créant des superclasses.".
    Ce genre de raisonnement ne peut que conduire à de l'héritage d'implémentation et à tous les problèmes qu'il pose. Dans ce cas, c'est sûr, il vaut mieux faire de la déléguation/composition.

    Mais utiliser systématiquement la déléguation/composition parce qu'on ne sait pas se servir de l'héritage correctement, ce n'est pas une solution non plus.

    Pour utiliser l'héritage correctement, c'est assez simple en pratique, il suffit de se laisser guider par les liens sémantiques entre les objets, sans se soucier de factoriser le code. Ensuite le reste découle tout seul.

  15. #15
    Membre expert
    Avatar de hed62
    Homme Profil pro
    Développeur .NET
    Inscrit en
    Juillet 2007
    Messages
    2 029
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 39
    Localisation : France, Nord (Nord Pas de Calais)

    Informations professionnelles :
    Activité : Développeur .NET
    Secteur : Santé

    Informations forums :
    Inscription : Juillet 2007
    Messages : 2 029
    Points : 3 134
    Points
    3 134
    Par défaut
    D'accord avec tout ce qui a été dis. Juste un point :
    Tu as un lien sémantique fort entre les deux que tu casses si tu travailles par déléguation
    Pas forcément. On peut faire une interface par forme, Rectangle implémentera IRectangle et IQuadrilatere, Carre implémentera IQuadrilatere, IRectangle et ICarre, ca permet de maintenir un peu de ce lien sémantique dont tu parles.
    Hervé Delannoy, Ingénieur études&développement.

    Je n'accepte pas les demandes de mise en relation MSN/yahoo sans motif.
    ------------------------------------------------------------------------
    Si , ni , ne peuvent vous aider, mais nous oui, pensez à un pti et au !
    Merci de vous relire
    ____________________________________________________________________________________
    Recherche joueurs de "Magic" sur Lille et environs.
    Donner plutôt que jeter.

  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
    J'en doute. Carre ne peut pas être compatible avec IRectangle à cause des invariants des carrés, et des possibilités offertes par IRectangle qui autorise des post-conditions de fonctions incompatibles avec l'invariant du carré. cf mon pseudo-code plus haut.

    En se creusant la tête il y a peut-être moyen de pondre des hiérarchies, mais vous me paressez passer à côté de l'"exercice" : on ne demande pas de trouver la bonne hiérarchie qui va bien entre carrés et rectangles, mais de comprendre ce que substituable veut dire. Et qu'en particulier "est-un" sont deux mots du français qui peuvent s'avérer être un faux ami quand on l'applique au monde OO.

    Et effectivement ces problèmes surgissent parce que l'on considère Carre et Rectangle comme des entités (et non des valeurs immuables) disposant de comportements.
    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...

  17. #17
    Expert confirmé
    Avatar de Hephaistos007
    Profil pro
    Enseignant Chercheur
    Inscrit en
    Décembre 2004
    Messages
    2 493
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : Enseignant Chercheur
    Secteur : Enseignement

    Informations forums :
    Inscription : Décembre 2004
    Messages : 2 493
    Points : 4 166
    Points
    4 166
    Par défaut
    On oublie trop souvent que la redéfinition de méthode est une composante essentielle du mécanisme d'héritage. Dans ton exemple Luc Hermitte, tu modifies volontairement les variables d'instances (largeur et hauteur) directement, sans passer par des mutateurs. Car évidemment, tu sais pertinemment qu'en redéfinissant* correctement setHauteur(float) et setLargeur(float) dans la classe carré, les invariants ne sont plus violés.

    (*) pour assurer la synchronisation hauteur/largeur
    Il vaut mieux mobiliser son intelligence sur des conneries que mobiliser sa connerie sur des choses intelligentes --- devise SHADOKS

    Kit de survie Android : mon guide pour apprendre à programmer sur Android, mon tutoriel sur les web services et enfin l'outil en ligne pour vous faire gagner du temps - N'oubliez pas de consulter la FAQ Android

  18. #18
    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
    Non. J'ai considéré que c'était des propriétés qui déclenchaient des appels à des mutateurs (Je ne l'avais pas dit explicitement).

    D'où le "- soit on viole la postcondition de f si la modification de la largeur du carré entraine celle de sa longueur".

    Quelles que soient les 2 modélisations Carré < Rectangle, ou Rectangle < Carré, il y a obligatoirement un contrat qui se fera violer.
    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...

  19. #19
    Expert confirmé

    Profil pro
    Leader Technique
    Inscrit en
    Juin 2005
    Messages
    1 756
    Détails du profil
    Informations personnelles :
    Âge : 46
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Leader Technique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juin 2005
    Messages : 1 756
    Points : 4 170
    Points
    4 170
    Par défaut
    Citation Envoyé par Luc Hermitte Voir le message
    En se creusant la tête il y a peut-être moyen de pondre des hiérarchies, mais vous me paressez passer à côté de l'"exercice" : on ne demande pas de trouver la bonne hiérarchie qui va bien entre carrés et rectangles, mais de comprendre ce que substituable veut dire.
    C'est ta vision des choses. On est la pour parler de l'héritage en général et en quoi on peut l'appliquer à Carré et Rectangle.

    L'héritage ne se limite pas au principe de substitution. Ce dernier n'est d'ailleurs qu'une condition à respecter si on veut pouvoir utiliser le polymorphisme.
    Il ne faut pas oublier que l'héritage dans le monde OO c'est avant tout une classification hérarchique des objets.

    La modélisation objet ne se limite pas à la programmation. C'est avant tout un outil d'analyse et de modélisation des problèmes. Le passage à l'implémentation de la solution n'est qu'une étape. Et elle ne se fera peut-être pas du tout en objet.
    L'exemple le plus flagrant est sans doute la conception d'une base de données relationnelle. On va établir un modèle conceptuel objet, qui utilisera l'héritage si besoin. Mais au moment de passer à l'implémentation, il faudra repasser sur un modèle E-R.

    C'est pourquoi il ne faut pas réduire l'héritage à la réutilisation du code. Il faut comprendre sa signification et l'importance du lien sémantique entre les objets. Tout comme il faut comprendre la signification d'une interface, et celle de la délégation.
    Lorsqu'on travaille par déléguation/composition, la plupart du temps ça ne correspond à aucune sémantique, c'est une technique d'implémentation pour ne pas avoir à faire de l'héritage multiple (si ce n'est pas pour réutiliser le code d'un autre composant qui en fait plus que ce qu'il devrait, ce qui interdit l'héritage).
    Cet usage a sa place dans un modèle d'implémentation, mais dans un modèle conceptuel on a de grandes chances de compliquer le modèle inutilement.

  20. #20
    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
    Citation Envoyé par Franck SORIANO Voir le message
    C'est ta vision des choses.
    Non. Je m'explique.

    "Un carré n'est pas un rectangle" est le sujet de l'article de Robert C. Martin, où il prend cet exemple pour expliquer le Principe de Substitution de Liskov, et que "est-un", malgré la vague ressemblance au langage naturel, est un terme qui représente un concept précis de la conception OO.

    Le but de l'article n'est pas de trouver la bonne classification (car elle dépendra du problème à résoudre), mais d'expliquer le LSP avec un exemple "choquant" (nos connaissances mathématiques) à l'appui.

    Carré vs rectangle n'est qu'un cas d'école peu utile comme tous les cas d'école. C'est pourquoi je préfère en général parler de Listes vs ListesTriees.

    PS: quand on a bien compris la dichotomie importation de code/substituabilité, l'héritage multiple passe comme une lettre à la poste. Quand l'héritage fait un peu de tout n'importe comment, c'est vite la catastrophe.
    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...

Discussions similaires

  1. Héritage, classe virtuelle
    Par Anium dans le forum C++
    Réponses: 3
    Dernier message: 21/05/2008, 09h18
  2. Héritage, classe de base
    Par Melem dans le forum Mise en page CSS
    Réponses: 4
    Dernier message: 25/02/2008, 15h45
  3. Héritage classe template->classe template
    Par zabibof dans le forum Langage
    Réponses: 5
    Dernier message: 11/08/2007, 11h05
  4. Réponses: 5
    Dernier message: 10/01/2007, 02h08
  5. Réponses: 4
    Dernier message: 26/01/2006, 10h48

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