Quel comportement ?
Le seul comportement que l'on puisse lui attribuer, c'est un comportement de changement des dimensions, ce qui implique plus d'héritage.
PS: Alp a relancé le débat dans le forum C++.
Quel comportement ?
Le seul comportement que l'on puisse lui attribuer, c'est un comportement de changement des dimensions, ce qui implique plus d'héritage.
PS: Alp a relancé le débat dans le forum C++.
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...
Behavior en anglais, c'est bien comportement en français. C'est bien de cela dont tu parles.IS-A relationship must refer to the behavior of the class!
Quel comportement ?
Le modèle de carré mathématique immuable n'a aucun comportement. Rien. Juste des contraintes et une immuabilité (difficilement un comportement)
Le modèle altérable de l'article a des comportements, mais il est incompatible avec un héritage LSP-ien.
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...
Salut,
Je trouve ces discussions super marrantes. Ce qui est dommage c'est que c'est pas facile à placer en société
. Comment dire à ma femme que ça fait 2 mois et demi que des internautes se demandent si un rectangle est un carré ou inversement, qu'on ne fait que de violer des règles. Espérons que goole fasse correctement le tri car sinon on va croire qu'il n'y a que des criminels sexuels sur ce site. Y'a peut-être déjà les RG qui scrutent le post. Et pendant ce temps La Crise passe.
J'aime bien la definition de l'heritage par: "un objet A herite de B si A est une sorte de B".
Mais est-ce qu'un carré est une sorte de rectangle ou inversement? Pas certain, c'est peut-être un abus de langage, car la notion d'heritage, pour moi, induit fatalement un niveau de détails plus élevé dans les enfants. Les propriétés communes remontent dans le parent.
Je dirai qu'il autant de différences entre une jument et un ane qu'un losange et un rectangle. L'union d'un losange (côtés de même longueur, paralleles deux à deux) et d'un rectangle (angles droits, paralleles deux à deux) donne un joli carré comme l'union (rare => cas particulier) d'une jument et d'un ane donne un mulet.
Je crois qu'on atteind avec ces formes géométriques les derniers stades d'évolution des quadrilatères > parallelogrammes convexes.
Du coup, le carré ne peut dériver ni du rectangle ni du losange. Un rectangle possede au moins deux propriétés: la longueur et la largeur. Le carré ne définit que le "côté" hérité de losange. Si carré herite de rectangle, il heritera de propriétés qui n'ont pas vraiment de sens logique. Si rectangle herite de carré alors rectangle peut utiliser la propriété côté de carré pour définir quoi... la largeur? Pas très logique... Idem pour les caractéristiques du losange.
Composer un carré à partir d'un rectangle? Pourquoi pas à partir un losange? Par contre, à partir d'un parallélogramme oui tout à fait. Et du coup on utilise les sommets et les côtés plutot que longueur et largeur. Le calcul de l'aire ne pose pas de pb ni le perimetre. Tout va bien. Si à un instant "t" l'etat du rectangle ou du losange permet un conversion en carré alors c'est cool.
A bien y reflechir l'heritage losange <-> carré <-> rectangle est "illogique" pas "abérant". C'est à dire qu'on peut bien y penser et le faire dans des cas particuliers.
Puré, il est tard.
A+
PS: Et que pensez-vous de la quadrature du cercle? Un carré de côté "c" peut-il etre converti en cercle de diametre "d" = "c"?
PS2: euh c'est quoi LSP?![]()
"Winter is coming" (ma nouvelle page d'accueil)
Suite au message de pseudocode, quelqu'un pourrait-il m'expliquer en quoi le code ci-dessous viole le LSP ?
(je ne connais pas JAVA, alors j'improvise un méli-mélo que tout un chacun comprendra sûrement, si je me réfère à la teneur des nombreux messages de cette discusion)
Merci.
Code java : 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 class Carré extends Rectangle { public Carré(int coté) { Rectangle(coté, coté); } public int getCoté() { return super.getLongueur(); /* ou .getLargeur() */ } public void setCoté(int coté) { // on maintient la cohérence géométrique super.setLongueur(coté); super.setLargeur(coté); } private void setLongueur(int longueur) { // le carré sait comment faire this.setCoté(longueur); } private void setLargeur(int largeur) { // le carré sait comment faire this.setCoté(largeur); } }
[EDIT] Avec ce code, il me semble que: (1) le carré est substituable à un rectangle; (2) le carré reste carré, même quand "celui" qui le modifie le perçoit comme instance de Rectangle; "il" sera peut être surpris de constater que largeur et longueur ne sont pas modifiables indépendamment. [/EDIT]
_
Le code est incomplet pour percevoir les problème, il manque sans la fonction cliente qui attend une post-condition sur altération de rectangle.
Retrouve mes premiers messages qui ont des assert dedans.
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...
L'override des méthodes setLongueur/setLargeur doit rester "public" afin que les méthodes du rectangle soient accessibles. C'est d'ailleurs là qu'interviennent les problèmes liés au LSP.(je ne connais pas JAVA, alors j'improvise un méli-mélo que tout un chacun comprendra sûrement, si je me réfère à la teneur des nombreux messages de cette discusion)
Tu as modifié le comportement des méthodes setLongueur et setLargeur. De ce fait, l'objet carré ne se comporte pas comme l'objet rectangle => violation du LSP.
ALGORITHME (n.m.): Méthode complexe de résolution d'un problème simple.
Alors c'est une erreur de conception de JAVA: ma conception viole LSP à cause du langage que j'utilise
Mais je ne vois pas pourquoi il y a violation.
Je n'ai pas modifié le comportement des méthodes SetLongueur et SetLargeur, mais j'ai ajouté une "contrainte".
Je m'explique: si j'attrape un objet Carré, que je l'envisage comme un objet Rectangle et que j'exécute SetLargeur, la succession d'appels sera:
Au final, la méthode SetLargeur de la classe rectangle est exécutée ==> au final, pas de modification du comportement si on considère SetLargeur.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5 rectangle.SetLargeur(123) /!\ ici liaison dynamique --> carré.SetLargeur(123) --> carré.SetCoté(123) --> rectangle.SetLongueur(123) /!\ ici liaison statique --> rectangle.SetLargeur(123) /!\ ici liaison statique
_
Ce n'est pas du tout un problème de Java[*]. Re-regarde le premier pseudo-code que j'ai codé.
Autre exemple : deux classes qui auront autant de membres (fonctions comme variables), mais des invariants différents: Conteneur (peu importe sa représentation interne), et ConteneurDeNombresPairs.
On ne peut pas définir l'une à partir de l'autre par un héritage qui permet syntaxiquement la substituabilité (héritages tout court en Java, héritage public en C++) tout en respectant le LSP.
Vous sauriez me faire la démonstration du conflit ?
[*] Java ne sait certes pas faire une héritage d'importation pure de code, mais c'est un détail de moindre importance.
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...
Ce n'est pas lié à Java, c'est lié à la notion d'héritage. Si tu hérites d'une classe, tu à l'obligation de laisser visibles les méthodes de la classe mère. Sinon, lors d'un "cast", on ne pourrait accèder aux méthodes !
Code java : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6 // cast d'un Carré en Rectangle : possible grace à l'heritage. Rectangle r = (Rectangle) moncarré; // changement de la longueur d'un rectangle r.setLongueur(5); // <---- causerait une erreur car la méthode est private dans la classe Carré !
Imaginons que je ne sache pas que mon instance de Rectangle est en fait un Carré. Tout ce que je sais c'est que je peux la manipuler comme un Rectangle. En particulier, si je double la longueur d'un seul de ses des cotés alors je double sa surface.Mais je ne vois pas pourquoi il y a violation.
Je n'ai pas modifié le comportement des méthodes SetLongueur et SetLargeur, mais j'ai ajouté une "contrainte".
Code java : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14 boolean doublementDeLaSurface(Rectangle r) { // calcul de la surface actuelle int oldsurface = r.getLongueur() * r.getLargeur(); // je double la longueur r.setLongueur( 2*r.getLongueur() ); // calcul de la nouvelle surface int newsurface = r.getLongueur() * r.getLargeur(); // normalement, dans un Rectangle, la surface a due doubler return (newsurface == 2*oldsurface); }
Si j'utilise une instance de la classe Rectangle, cela fonctionne:
Code java : Sélectionner tout - Visualiser dans une fenêtre à part
1
2 Rectangle r = new Rectangle(3,5); Assert.assertTrue(doublementDeLaSurface(r)==true); // <-- sucess
Mais si j'utilise une instance de la classe Carré (castée en Rectangle) cela ne marchera pas. Le Carré ne se comporte pas comme un Rectangle.
Code java : Sélectionner tout - Visualiser dans une fenêtre à part
1
2 Rectangle r = (Rectangle) new Carré(4); Assert.assertTrue(doublementDeLaSurface(r)==true); // <-- failure
C'est ce que dit le LSP : "une instance de la classe fille (carré) DOIT se comporter comme une instance de la classe mère (rectangle)"
ALGORITHME (n.m.): Méthode complexe de résolution d'un problème simple.
Bienvenu au club immobilis et =JBO=. Je me suis cassé les dents
sur le sujet.
J'ai trouvé ce lien en français qui parle du rectangle et du carré:
http://blog.emmanueldeloget.com/inde...tion-de-liskov
D'après le principe d'ouverture et fermeture, le code est ouvert à l'extention fermé à la modification. Si on utilise l'héritage, ce serait en vue d'étendre les capacités d'une classe et dans le cas du carré on passe de "deux variables" à une seule variable ce qui restreint les possibilités initiales de rectangle.
Une des principales leçon dont je tire de l'héritage c'est qu'il n'est pas compatible avec la spécialisation au sens on bride des comportements de la classe mère. Du moins, c'est l'idée à présent que je me fais de la notion d'héritage.
L'héritage doit être utilisé pour ajouter des nouveaux comportements, pas pour modifier les comportements existants.
Code java : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6 class Rectangle { /* ... */ } class RectanglePlein extends Rectangle { /* ... */ } class RectanglePleinAvecBordure extends RectanglePlein { /* ... */ }
ALGORITHME (n.m.): Méthode complexe de résolution d'un problème simple.
OK, je m'incline...
Au passage, je remarque qu'il faut bien faire la distinction entre "hériter" et "spécialiser" (ou dériver).
Dans mon petit code, j'abordais les choses sous l'angle de la spécialisation sans tenir compte de l'héritage.
Pour répondre à la question initiale:
Le prof aurait dû dire: «la classe carré spécialise la classe rectangle.»
Et encore merci de toutes vos précisions (assertions).
[EDIT]
P.S.
Après quelques temps, je regrette ce que j'ai écrit (un peu vite) dans ce message en utilisant maladroitement le verbe "spécialiser" qui a une trop forte connotation "objet" et "héritage". Cette maladresse a été ensuite relevée dans différents messages de la discussion et aussi ailleurs.
Cependant, intuitivement, je voulais faire ressentir le fait que le carré n'est pas "tellement différent" d'un rectangle puisque certains le définissent en se basant justement sur un rectangle : «un carré est un rectangle qui...».
Peu importe si ce n'est pas d'une rigueur absolue, du moment que cela suffit pour le besoin du moment... c'est aussi une manière d'abstraire que de se limiter à certains aspects saillants.
J'avais donc en tête le verbe "dériver" qui lui est suffisamment floupour me satisfaire et ne pas heurter les "dogmes" LSPiens qui s'imposent et qui appliquent "un droit de licence" à l'utilisation du verbe "spécialiser".
Aussi dans une nouvelle tentative pour retrouver mon honneur perdu, je vais tenter de reformuler ma pensée :
Le prof aurait pu dire: «la classe carré est dérivable de la classe rectangle»...ce qui nous ramène à un simple choix de modélisation et nous permet de continuer à développer comme on en a envie !
[/EDIT]
_
"Winter is coming" (ma nouvelle page d'accueil)
Il faut comprendre que si OCP est respecté alors LSP est respecté, donc ouvert à l'exention du code mais pas à la modification.le principe de substitution de Liskov (LSP, pour Liskov substitution principle), un principe souvent décrit comme corolaire de OCP et énoncé par deux femmes
Un carré n'offre pas la même interface que rectangle d'une part, et rectangle est un état particulier de rectangle (état spécial).
J'ai lu un article sur la génie génétique cette semaine où l'on parle de cellule souche et de cellule spécialisées (foie, peau, neurones, etc ...) dans le cardre de la thérapie génétique pour remplacer des organes à partir de cellules du malade.
Au départ, lorsque que l'embryon se développe, on part des cellules souches (capables de spécialisées en n'importe quelle catégorie de cellule spécialisée (200). Mais au fil des réplications cellulaires en se spécialisant elles perdent leur caractère générique. Si on devait appliqué OO, en utilisant l'héritage, on serait confronté aux mêmes problèmes. C'est comme ça que j'entend spécialisation opposé à généralisation dans le sens où la classe fille n'est plus capable de réaliser les mêmes comportements de la cellule souche. Et pourtant, il est possible de revenir à l'état de cellule souche par des méthodes "barbares" en utilisant notament le virus du SIDA pour rétablir la structure originelle de l'ADN de la cellule souche (état initial).
hum... on peut construire une classe qui respecte l'OCP mais pas le LSP. Enfin bon, c'est sale.
En objet, une classe est spécialisée si elle sait faire TOUT ce que fait sa classe mère, ET PLUS ENCORE.C'est comme ça que j'entend spécialisation opposé à généralisation dans le sens où la classe fille n'est plus capable de réaliser les mêmes comportements de la céllule souche.
ALGORITHME (n.m.): Méthode complexe de résolution d'un problème simple.
Au moins, j'ai retenu quelques trucs.hum... on peut construire une classe qui respecte l'OCP mais pas le LSP. Enfin bon, c'est sale.![]()
J'ai vu l'exemple du carré/rectangle : largeur/longueur -> coté
J'ai vu l'exemple du cercle/ellipse : a,b - > r
J'ai vu l'exemple du bird/pingoin : vole -> ne peux pas voler
Dans chacun des cas, il y a perte ou simplification, en fait il s'agirait plus de simplification/réduction et non de spécialisation finalement.
Je pourrais prendre les cours de physiques, on part d'équations différentielles générales, et puis on fait des simplifications, finalement l'équation à l'arrivée ne resemble plus guère à l'équation d'origine, mais on a l'outil mathématique pour le résoudre dans le cas précis, mais on ne peut pas utiliser cette solution dans le cas général, en bref des modifications à chaque fois par rapport à la classe ancêtre.
Vous continuez de vous attacher à des détails non pertinents. Recommençons avec: Conteneur/ConteneurDeNombresPairs. 0 perte de membres: mêmes attributs, mêmes opérations.
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...
Un Conteneur est un espace fini alors que ConteneurNombreDeNombrePairs ne l'est pas, donc l'héritage ne marche pas, intuitivement c'est ma première opinion.
Partager