![]() |
| Le forum de référence en programmation et développement. Articles, cours et tutoriels du débutant au chef de projet et DBA confirmé. | |||||||
|
|||||||
| C++ Forum d'entraide technique sur le langage C++. Avant de poster -> F.A.Q C++ |
![]() |
|
|
Outils de la discussion |
|
|
#1 (permalink) |
![]() Date d'inscription: juin 2005
Messages: 6 307
|
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
__________________
Mon blog (en) - Mes articles et critiques de livres - Brute C++ - Qt - Algo - Intelligence Artificielle Pour participer, contactez-moi A la recherche d'un emploi à temps partiel si possible (Software, Web, ...) You see things as they are and ask, 'Why?' I dream things as they never were and ask, 'Why not?' (George Bernard Shaw). |
|
|
|
|
|
#3 (permalink) |
![]() Date d'inscription: juin 2005
Messages: 6 307
|
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.
__________________
Mon blog (en) - Mes articles et critiques de livres - Brute C++ - Qt - Algo - Intelligence Artificielle Pour participer, contactez-moi A la recherche d'un emploi à temps partiel si possible (Software, Web, ...) You see things as they are and ask, 'Why?' I dream things as they never were and ask, 'Why not?' (George Bernard Shaw). |
|
|
|
|
|
#4 (permalink) |
|
Membre à l'essai
![]() Date d'inscription: août 2008
Localisation: La semaine sur Brest, le week-end sur Caen :)
Âge: 22
Messages: 42
|
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. |
|
|
|
|
|
#5 (permalink) | |
![]() Date d'inscription: juin 2005
Messages: 6 307
|
Citation:
__________________
Mon blog (en) - Mes articles et critiques de livres - Brute C++ - Qt - Algo - Intelligence Artificielle Pour participer, contactez-moi A la recherche d'un emploi à temps partiel si possible (Software, Web, ...) You see things as they are and ask, 'Why?' I dream things as they never were and ask, 'Why not?' (George Bernard Shaw). |
|
|
|
|
|
|
#6 (permalink) |
|
Membre à l'essai
![]() Date d'inscription: août 2008
Localisation: La semaine sur Brest, le week-end sur Caen :)
Âge: 22
Messages: 42
|
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 :
class BaseStream { protected: /// Le flux est-il ouvert ? bool mOpen; public: /** * Constructeur par défaut. */ BaseStream(void) {} /** * Destructeur. */ virtual ~BaseStream(void) {} /** * Ouvre le stream. * @Param _overWrite : mode d'ouverture du flux. * @Retour True si l'ouverture à réussie, false sinon. */ virtual bool open(bool _overWrite) = 0; /** * Lecture d'une portion des données du flux vers le buffer.. * @Param _buffer : buffer qui va contenir les données inscrite dans le flux. * _count : taille en bytes à lire dans le flux. * @Retour Nombre de bytes lus. */ virtual size_t read(char* _buffer, size_t _count) = 0; /** * Ecriture d'une portion des données du buffer vers le flux. * @Param _buffer : buffer qui va contenir les données à inscrire dans le flux. * _count : taille en bytes à inscrire dans le flux. * @Retour Nombre de bytes écris. */ virtual size_t write(const char* _buffer, size_t _count) = 0; /** * Indique si la fin du flux est atteinte. * @Retour True si c'est la fin, false sinon. */ virtual bool isEndOfStream(void) const = 0; /** * Indique si le flux est ouvert. * @Retour True s'il l'est, false sinon. */ virtual bool isOpen(void) const = 0; /** * Ferme le flux. */ virtual void close(void) = 0; }; 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. |
|
|
|
|
|
#7 (permalink) |
|
Membre Expert
![]() Date d'inscription: octobre 2006
Messages: 1 072
|
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 |
|
|
|
|
|
#8 (permalink) |
![]() |
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
__________________
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 Vous avez obtenu votre réponse en bas de pageA méditer: La solution la plus simple est toujours la moins compliquée |
|
|
|
|
|
#9 (permalink) | |
|
Expert Confirmé Sénior
![]() ![]() Date d'inscription: août 2003
Localisation: Toulouse
Messages: 3 449
|
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 Les MP ne sont pas une hotline, ne vous attendez pas à ce que l'on réponde à vos questions techniques par MP. Dernière modification par Luc Hermitte ; 08/11/2008 à 23h19 |
|
|
|
|
|
|
#10 (permalink) |
|
Provisoirement toléré(e)
Date d'inscription: décembre 2003
Messages: 3 092
|
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 |
|
|
|
|
|
#11 (permalink) |
|
Membre Confirmé
![]() Date d'inscription: septembre 2008
Messages: 263
|
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».
__________________
Socoa, Bibliothèque d'analyse de code source C++ (développement en cours) |
|
|
|
|
|
#12 (permalink) | |
|
Expert Confirmé Sénior
![]() ![]() Date d'inscription: août 2003
Localisation: Toulouse
Messages: 3 449
|
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 Les MP ne sont pas une hotline, ne vous attendez pas à ce que l'on réponde à vos questions techniques par MP. |
|
|
|
|
|
|
#13 (permalink) |
|
Membre Confirmé
![]() Date d'inscription: septembre 2008
Messages: 263
|
1) NVI, mmh ? Je vais aller voir ça
2) Bien sûr
__________________
Socoa, Bibliothèque d'analyse de code source C++ (développement en cours) |
|
|
|
|
|
#14 (permalink) |
![]() |
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
__________________
Partager grâce à l'open source et aux logiciels libres. "Never use brute force in fighting an exponential." (Andrei Alexandrescu) Conseils perso en vrac 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 |
|
|
|
|
|
#15 (permalink) | |||
|
Expert Confirmé Sénior
![]() ![]() Date d'inscription: novembre 2005
Messages: 3 881
|
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. |
|||
|
|
|
|
![]() |
![]() |
||
[POO] Suivez-vous le principe de substitution de Liskov ?
|
||
| Outils de la discussion | |
|
|