|
Publicité | |||||||||||||||||||||||
|
|
#1 |
![]() ![]() Inscription : juin 2005 Messages : 8 554 ![]() |
Bonjour,
Dans un monde où la POO est employée à toutes les sauces, dont certaines sont mauvaises, on voit des principes essentiels de programmation orientée objet bafoués. L'un d'entre eux est le Principe de Substitution de Liskov (Liskov Substitution Principle, LSP). En quoi consiste-t-il ? Introduit en 1987[1] par Barbara Liskov, le principe de substitution de Liskov est, formellement, le suivant : Si une propriété P est vraie pour une instance x d'un type T, alors cette propriété P doit rester vraie pour tout instance y d'un sous-type de T où dans notre cas les sous-types de T sont les classes qui dérivent de T. Moins formellement, il s'agit en fait que l'on puisse remplacer dans un code source toute instance de type Mere par une instance d'un type Fille qui dérive de Mere sans altérer en quoique ce soit la caractère correct du programme. La question est donc : suivez-vous ce principe dans vos hiérarchies de classes C++ ? Que pensez-vous de ce principe ? Lui trouvez-vous une utilité ? (j'espère [1] Voir : http://citeseer.ist.psu.edu/cache/pa...ov94family.pdf
__________________
/!\ A French community for Haskell /!\ Mon blog anglais - Mes articles et critiques de livres - FAQ C++0x, avec liste des nouveautés - Conseils sur le C++ - La meilleure FAQ du monde - Avant de créer des classes que vous réutiliserez, regardez si ça n'existe pas déjà - Le site du comité de normalisation du C++ Le guide pour bien débuter en C++ |
|
00
|
|
|
#2 |
|
Membre Expert
![]() Inscription : octobre 2006 Messages : 1 325 ![]() |
Avec la virtualité si on remplace les instances de type Mère par les instances de type Fille, en changeant rien d'autre, le programme aura très souvent un comportement différent.
|
|
|
00
|
|
|
#3 |
![]() ![]() Inscription : juin 2005 Messages : 8 554 ![]() |
Oui bien sûr, c'est le principe même de polymorphisme. Mais le programme se comportera correctement, c'est ça l'idée.
Il s'agit de garder un programme correct en interchangeant des types dans une hiérarchie, cf le 1er post. Parfois, lorsqu'une classe B ne devrait pas hériter d'une classe A mais dont elle hérite quand même, on se retrouve avec un programme incohérent. C'est plutôt répandu comme violation du LSP.
__________________
/!\ A French community for Haskell /!\ Mon blog anglais - Mes articles et critiques de livres - FAQ C++0x, avec liste des nouveautés - Conseils sur le C++ - La meilleure FAQ du monde - Avant de créer des classes que vous réutiliserez, regardez si ça n'existe pas déjà - Le site du comité de normalisation du C++ Le guide pour bien débuter en C++ |
|
00
|
|
|
#4 |
|
Membre habitué
![]() Mickaël LeclercIngénieur développement logiciels Inscription : août 2008 Messages : 219 ![]() |
Je ne connaissais pas ce principe, enfin je l'utilise souvent, mais je ne savais pas que c'était un principe de la POO et qu'il avait un petit nom
Maintenant est-ce que le programme va se comporter correctement, je pense que tout dépend de ce que l'on fait dans les classes filles. Il pourrait très bien se comporter correctement, mais ne donnera pas le résultat voulu. |
|
00
|
|
|
#5 | |
![]() ![]() Inscription : juin 2005 Messages : 8 554 ![]() |
Citation:
__________________
/!\ A French community for Haskell /!\ Mon blog anglais - Mes articles et critiques de livres - FAQ C++0x, avec liste des nouveautés - Conseils sur le C++ - La meilleure FAQ du monde - Avant de créer des classes que vous réutiliserez, regardez si ça n'existe pas déjà - Le site du comité de normalisation du C++ Le guide pour bien débuter en C++ |
|
|
00
|
|
|
#6 | ||
|
Membre habitué
![]() Mickaël LeclercIngénieur développement logiciels Inscription : août 2008 Messages : 219 ![]() |
On ne peut pas le garantir alors, tout dépend de la finalité de la classe fille, ce qu'elle va gérer. Je prend l'exemple d'un flux avec une classe mère comme ceci
Code :
Disons que FileStream est un flux charger de gérer un fichier (donc sur un disque physique) et DataStream est un flux contenant un ensemble de données gérer dans un buffer (en mémoire). Le but est de charger une ressource. Dans notre cas que la ressource soit présente sur le disque dur (fichier xml par exemple) ou en mémoire le résultat final sera le même, tout ce que l'on veut c'est charger une ressource, donc on peut dire ici que le programme devrait fonctionner correctement. |
||
|
00
|
|
|
#7 |
|
Membre Expert
![]() Inscription : octobre 2006 Messages : 1 325 ![]() |
Je le bafoue : j'exige des paramètres dans le constructeur ou une initialisation différente pour certaines classes dérivées -- éventuellement transtypées ensuite en classes de base.
Mais je pense pas que je doive changer mon code |
|
|
00
|
|
|
#8 |
![]() ![]() |
Salut,
J'essaie - généralement - de le respecter, et principalement lorsque j'essaye d'expliquer ce qu'est l'héritage. C'est la raison pour laquelle j'insiste sur la valeur "sémantique" des termes utilisés pour représenter deux classes pour lesquelles l'héritage est envisagé, en essayant d'inciter la personne à se poser la question de savoir "s'il est sémantiquement correct de dire que la classe B est réellement une 'amélioration' de la classe A, ou s'il s'agit d'un objet se contentant d'avoir les mêmes caractéristiques". Sémantiquement, parlant, il est effectivement correct de dire que "l'homme est un (animal) mammifère", alors que, effectivement, bien que les caractéristiques soient identique, il est impossible de prétendre qu'une pile (qui utilise, rappelons le, le principe LIFO) est une file (qui utilise, elle, le principe FIFO), ou vice-versa. Ceci n'empêchant nullement de trouver une "point sémantiquement commun" dans une autre classe que nous pourrions nommer "structure dynamique" De la même manière, et malgré les écueils qui risquent quasi immanquablement de se présenter, je n'ai aucune difficulté à admettre l'héritage multiple: il est sémantiquement correct de dire q'une turbo-génératrice est une génératrice, tout comme il est sémantiquement correct de dire que c'est un turbine
__________________
en bas de page |
|
|
00
|
|
|
#9 | |
|
Expert Confirmé Sénior
![]() ![]() Inscription : août 2003 Messages : 4 252 ![]() |
On pourrait pinailler sur le fait que Barbara Liskov a introduit une définition, et que l'on a tiré un principe de cet article. Mais ce n'est pas important.
Citation:
La vérité est que l'héritage fut vendu pour de mauvaises raisons (i.e. importer du code), et que beaucoup en sont restés là -- et ne savent pas s'en servir (alors imaginez l'héritage multiple). Avec une bonne compréhension et un respect du LSP, l'essentiel des critiques faites à l'encontre de l'héritage multiple s'évanouissent. Autrement, si dans une hiérarchie je n'ai pas de substituabilité, je passe en héritage privé (merci le C++) et basta: j'ai ainsi importé du code sans avoir fragilisé mon système en permettant des substitutions qui n'ont pas de sens ou qui sont instables. NB: Il est difficile de ne pas rapprocher le LSP de la programmation par contrats. Cf l'article de Robert C. Martin où il explique le LSP en s'appuyant sur des carrés et des rectangles. Cf aussi le fait qu'une liste triée, n'est pas une liste. Sujet déjà bien abordé par ici
__________________
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. Dernière modification par Luc Hermitte ; 08/11/2008 à 23h19. |
|
|
|
00
|
|
|
#10 |
|
Expert Confirmé Sénior
![]() Mathias GaunardIngénieur développement logiciels Inscription : décembre 2003 Messages : 3 535 ![]() |
Il faut aussi faire attention aux invariants dans l'autre sens. Si tu as un Carré, sous-type d'un Rectangle, il ne faut pas qu'il soit possible de modifier l'objet par sa vue Rectangle de manière à ce que les invariants de Carré ne soient plus satisfaits.
La solution évidente dans ce cas est d'avoir des objets immutables. J'utilise personnellement peu l'héritage et préfère les concepts. L'affinement de concepts est similaire à la dérivation. Mais lorsqu'on utilise les concepts, ce genre de propriétés est vraiment évident. D'autant plus qu'on ne manipule que des interfaces non-intrusives, et non pas des classes.
__________________
Boost ftw |
|
|
00
|
|
|
#11 |
|
Membre chevronné
![]() ![]() Inscription : septembre 2008 Messages : 667 ![]() |
Ce principe n'est ni plus ni moins que la raison d'être des interfaces.
C'est pour cela qu'on dit souvent «Préférez la composition à l'héritage».
__________________
Cours : Initiation à CMake Projet : Scalpel, bibliothèque d'analyse de code source C++ (développement en cours) Ce message a été tapé avec un clavier en disposition bépo. |
|
|
00
|
|
|
#12 | |
|
Expert Confirmé Sénior
![]() ![]() Inscription : août 2003 Messages : 4 252 ![]() |
Citation:
Oui pour la deuxième affirmation si on restreint l'affirmation à l'héritage public. L'héritage privé convient parfaitement pour importer du code sans permettre d'être utilisable en place de (après tout nous sommes sur un forum C++ ; Java/C# en sont incapables, ruby a un autre mot clé pour, etc.). Le choix héritage privé / composition dépendant d'aspects liés au niveau de couplage acceptable, au type de fainéantise autorisée (après tout, c'est un héritage méconnu), ...
__________________
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. |
|
|
|
00
|
|
|
#13 |
|
Membre chevronné
![]() ![]() Inscription : septembre 2008 Messages : 667 ![]() |
1) NVI, mmh ? Je vais aller voir ça
2) Bien sûr
__________________
Cours : Initiation à CMake Projet : Scalpel, bibliothèque d'analyse de code source C++ (développement en cours) Ce message a été tapé avec un clavier en disposition bépo. |
|
|
00
|
|
|
#14 |
![]() ![]() |
1) NVI= Non virtual interface. En rapide, ne pas rendre virtuelles les fonctions membres publiques qui devrait l'être mais préférer qu'elles appelent des fonctions membres protected qui elles le serront
__________________
"Never use brute force in fighting an exponential." (Andrei Alexandrescu) Mes articles dont Conseils divers sur le C++ Une très bonne doc sur la STL (en) Why linux is better (fr) Dernière modification par Davidbrcz ; 09/11/2008 à 13h18. |
|
|
00
|
|
|
#15 | |||
|
Expert Confirmé Sénior
![]() ![]() ![]() Inscription : novembre 2005 Messages : 4 760 ![]() |
Citation:
la conception de sa réalisation et que si la notion abstraite de hiérarchie de type va s'implémenter avec de la hiérarchie de classes et alors respecter ce principe, je ne m'interdit pas d'utiliser le mécanisme C++ d'héritage pour autre chose -- auquel cas le principe pourrait ne pas être respecté (mais je n'ai pas de cas sous la main). Je ne suis pas sûr de te comprendre. D'une part en ce qui concerne le sujet de la discussion, quand tu ajoutes: Citation:
principe de substitution devrait s'appliquer exactement de la même manière. Si tu écris cela d'une manière plus générale, je ne sais pas de quoi tu parles précisément: il ne me semble pas que j'ai à faire un choix entre les deux. Est-ce que tu veux dire que tu préfères le polymorphisme paramétrique au polymorphisme d'inclusion? Mais les concepts sont justement de mon point de vue un mécanisme de polymorphisme d'inclusion (d'où l'applicabilité du LSP). Est-ce que tu veux dire que tu préfères l'utilisation de mécanismes statiques aux mécanismes dynamiques? Quand j'ai le choix aussi, mais les mécanismes dynamiques sont plus généraux et on n'a pas toujours le choix. Citation:
composition? (Il y a au moins une autre différence, purement technique, qui fait qu'on peut utiliser l'un ou l'autre: l'ordre d'initialisation par rapport aux bases).
__________________
Les MP ne sont pas là pour les questions techniques, les forums sont là pour ça. Par contre, un MP convient bien pour postuler ou demander des informations complémentaires pour ce stage. |
|||
|
|
00
|
|
|
#16 | |
|
Expert Confirmé Sénior
![]() Mathias GaunardIngénieur développement logiciels Inscription : décembre 2003 Messages : 3 535 ![]() |
Citation:
__________________
Boost ftw |
|
|
|
00
|
|
|
#17 |
|
Expert Confirmé Sénior
![]() ![]() ![]() Inscription : novembre 2005 Messages : 4 760 ![]() |
Les concepts sont plus qu'une interface, ils associent une sémantique à celle-ci. Si tu affines un concept sur une base purement syntaxique (présence des interfaces requises), tu as un problème et c'est celui que le LSP cherche justement à éviter.
__________________
Les MP ne sont pas là pour les questions techniques, les forums sont là pour ça. Par contre, un MP convient bien pour postuler ou demander des informations complémentaires pour ce stage. |
|
|
00
|
|
|
#18 |
|
Expert Confirmé Sénior
![]() Mathias GaunardIngénieur développement logiciels Inscription : décembre 2003 Messages : 3 535 ![]() |
Les concepts sont par défaut nominaux. Seuls les concepts automatiques sont implicites et se basent sur des propriétés structurelles comme la syntaxe.
Enfin bref, je vois pas où tu veux en venir.
__________________
Boost ftw |
|
|
00
|
|
|
#19 |
|
Expert Confirmé Sénior
![]() ![]() ![]() Inscription : novembre 2005 Messages : 4 760 ![]() |
L'affinement des concepts est une relation de sous-type. Les éléments sont les types du C++, les catégories les concepts -- le fait que les catégories soient nominales ou structurelles n'a aucun rapport avec le sujet.
__________________
Les MP ne sont pas là pour les questions techniques, les forums sont là pour ça. Par contre, un MP convient bien pour postuler ou demander des informations complémentaires pour ce stage. |
|
|
00
|
|
|
#20 |
|
Expert Confirmé Sénior
![]() Mathias GaunardIngénieur développement logiciels Inscription : décembre 2003 Messages : 3 535 ![]() |
Oui, rien de nouveau.
Je ne vois toujours pas où tu veux en venir.
__________________
Boost ftw |
|
|
00
|
Copyright © 2000-2012 - www.developpez.com