Citation:
Envoyé par
Médinoc
Cela me parait bizarre, car contraire à la covariance et la contravariance.
Selon ces principes, une classe dérivée est en droit d'étendre les pré-conditions (contravariance pour les paramètres, non-possible par C++) et de restreindre les post-conditions (covariance pour la valeur de retour, autorisée en C++).
A vrai dire, je ne vois pas où il pourrait y avoir un problème...
Partons de la définition, courremment acceptée, de l'héritage comme étant une relation "est-un".
Cette définition est suffisamment "imprécise" pour que l'on puisse la répartir en trois situations distinctes (bien que les deux premières puissent, dans une certaine mesure, s'associer pour partie des comportements avec la troisième ;)):
- La classe dérivée est une spécialisation de la classe de base (le chien qui est un animal)
- La classe dérivée est une généralisation de la classe de base (je n'ai pas d'exemple, mais ca existe)
- La classe dérivée est une réalisation de la classe de base (elle permet juste la "construction" de la classe de base avec des données particulières: cf la hierarchie d'exception proposée plus haut)
Dans le premier cas, il semble cohérent d'accepter qu'une condition (qu'elle soit vérifiée avant ou après ne change pas grand chose) soit plus restrictive que la condition de départ.
Dans le second cas, il semble cohérent d'accepter qu'une condition soit plus laxiste que celle de départ, et dans le dernier cas, la condition doit être strictement identique à celle de départ.
D'un autre côté, une condition (de manière générale) est destinée à donner le "go" (l'objet est cohérent) ou le "stop" (l'objet n'est pas cohérent), éventuellement assortie d'information permettant de récupérer un état cohérent.
La seule différence entre pré et post condition est que, la pré condition vérifie que nous sommes bien face à la possibilité d'effectuer une action alors que la post condition nous indique que l'action effectuée a "merdé", et qu'il faut annuler les modifications éventuellement apportées.
Il ne me semble donc pas aberrant d'accepter, selon le sens d'héritage envisager qu'une condition puisse s'adapter au type réel de l'objet utilisé, et ce, quel que puisse être sa "position" dans la logique ;)
Maintenant, il se peut que ce raisonnement - peut être un peu trop théorique - ne tienne pas la route, mais, dans ce cas, dites moi où, que je me couche moins bête ce soir que ce que je ne l'étais en me levant ce matin ;):D
Citation:
Envoyé par
white_tentacle
Problème, rien dans le langage ne me le garantit. Et la pratique tend à prouver que ce qui n'est pas garanti, est faux.
Là, on attaque le problème majeur de la compétence (ou de l'incompétence) du concepteur... et il n'y a effectivement rien qui puisse te prémunir de sa bêtise ;)
Citation:
Non, justement, je veux l'empêcher !
Ca, c'est un problème de communication ;)...
La gestion de la qualité passe aussi par le fait que la communication doit pouvoir passer de manière non ambigüe en évitant toute interprétation ;)
Si tu savais le nombre de fois où j'ai pesté sur le phénomène du "téléphone sans fil", y compris dans des domaines qui n'ont rien à voir avec l'informatique...
Mais, encore une fois, cela démontre la (non) compétence du gestionnaire ;)
Citation:
Mon but, c'est que si je suis sûr qu'appeler f() sur un A va réussir, alors, appeler f() sur un B va réussir aussi. La programmation par contrat me garantit ça, car elle garantit que B::f demande au plus que les préconditions de A::f soient respectées.
Mais je ne vois pas en quoi l'adaptation du comportement au type réel, pour autant qu'elle n'inverse pas la logique (de renvoyer vrai alors qu'elle aurait du renvoyer faux) serait de nature à empêcher que l'appel de f() ne réussisse...
Citation:
Je crois que si tu penses ça, tu passes à côté du but du LSP. Ou alors, c'est moi qui n'ai rien compris au LSP ;).
Comme indiqué plus haut LSP ne s'applique qu'aux invariants, aux propriétés intrinsèques de la classe de base, qui sont transmises, sans modification, aux classes dérivées.
Sinon, dés que tu envisage le polymorphisme, tu en viens à estimer ne plus respecter LSP ;)
Citation:
Quand je code une fonction qui prend un A& en paramètre, je n'ai aucune idée du type réel qui m'est passé. Je sais juste qu'il est A ou dérive de A. Je peux donc vérifier les contrats définis par A, pas plus. Si on me passe un B, et qu'il vérifie les contrats de A, pas de problème. Mon algorithme va fonctionner. Si on me passe un C, qui ne vérifie pas les contrats de A, mais est plus restrictif, ça risque fortement de merder.
Le LSP me dit que, si je peux substituer un C à un A, alors, tout algorithme qui fonctionnait pour un A, fonctionne pour un C. Ça s'applique complètement aux comportements polymorphes, du moment qu'on respecte le contrat !
Mais qui dit que tu ne peux pas adapter le contrat au type réel :question:
Le contrat est là pour baliser les conditions dans lesquelles l'algorithme fonctionne, mais si tu adapte (quelle qu'en soit la manière) le contrat au type réel parce que l'algorithme peut fonctionner avec des valeurs différentes si tu ne manipule pas effectivement un objet du type de la classe de base, il n'y a pas de raison que ton algorithme devienne faux ;)
Citation:
Un héritage qui restreindrait les préconditions, rend faux tout algorithme qui se basait sur ces préconditions.
Pas forcément... Pour autant que tu adapte la condition de manière cohérente ;)
Citation:
Un héritage qui étend ces préconditions, lui, n'empêche pas ces algorithmes de fonctionner (c'est l'inverse pour les postconditions).
pas moins que s'il les restreignait ;)
Citation:
Une condition non-polymorphe, c'est une restriction inutile. Le tout est de savoir dans quel sens tu as le droit de modifier ta condition :
- les préconditions peuvent être étendues (ce n'est pas parce que A::f n'accepte pas quelque chose, que B::f n'a pas le droit de l'accepter)
- les postconditions peuvent être restreintes (si A::f garantit quelque chose, B::f a le droit de garantir quelque chose de plus)
- les invariants peuvent être restreints (comme les postconditions)
Non... une condtion peut très bien être que le diviseur ne soit pas nul...
Cette condition n'a aucun besoin d'être polymorphe, du moins, si l'on excepte la possibilité de manipuler des types primitifs différents, mais trouve sa pleine utilité dans n'importe quelle division si tu veux éviter les erreur de "division par 0" ;)