Principe de substitution

Nous avons vu différents types d'abstraction, et nous allons nous intéresser à l'abstraction par hiérarchie de type, qui est un des piliers des concepts de l'orienté-objet.

Hiérarchie de classe, héritage

En orienté-objet, l'héritage permet par exemple de définir des comportements minimum pour une classe, et d'ajouter les comportements spécifiques dans des classes enfants, qui héritent (dérivent) de la classe parent.

Parmi les avantages de cette manière de programmer, nous pouvons retenir qu'il n'est plus nécessaire de re-définir dans les classes enfants ce qui est déjà défini dans les classes parents. Ceci nous permet une abstraction par hiérarchie de type, c'est à dire que nous pouvons considérer les différents enfants comme étant du type parent.

Un des exemple souvent utilisé pour expliquer l'héritage est issu de la géométrie :

  • Un quadrilatère possède 4 côtés
    • Un parallélogramme possède aussi 4 côtés, c'est un quadrilatère.
      Il est cependant particulier, car il possède en plus la particularité d'avoir des côtés opposés de même longueur.
      • Un rectangle possède aussi 4 côtés et des côtés opposés de même longueur, c'est un quadrilatère, et même un parallélogramme.
        Il est cependant particulier, car il possède en plus la particularité d'avoir 4 angles droits.
        • Un carré possède toutes les caractéristiques d'un rectangle, c'est donc un rectangle.
          Il est cependant particulier, car il possède en plus la particularité d'avoir 4 côtés de même longueur.

Ce type de classement est tout à fait correct au niveau de la sémantique et selon le point de vue de la géométrie, et naturellement c'est de cette manière que nous construisons notre hiérarchie de classes en programmation.

Cependant, Liskov soulève un problème au niveau du comportement attendu, car notre structure renforce à chaque fois les contraintes nécessaires à la création d'une forme par rapport à sa forme parent.

Contents Haut

Principe de substitution, en bref

Let q(x) be a property provable about objects x of type T. Then q(y) should be true for objects y of type S where S is a subtype of T.
« Si q(x) est une propriété démontrable pour tout objet x de type T, alors q(y) est vraie pour tout objet y de type S tel que S est un sous-type de T. »
Barbara Liskov2et Jeannette Wing3

Contents Haut

Sémantique et substitution de Liskov

La sémantique nous pousse à utiliser une hiérarchie comme nous l'avons vu dans notre exemple du carré qui hérite des propriétés du rectangle, ce qui est logique car un carré est au moins un rectangle.

Cependant, si nous définissons dans la classe rectangle les méthodes suivantes affecteLargeur(entier largeur), affecteHauteur(entier hauteur), et entier donneSurface(), la méthode de calcul de la surface renvera la largeur multipliée par la hauteur. C'est correct.

Selon Liskov, le carré doit pouvoir à tout moment se comporter comme un rectangle. Notre exemple semble fonctionner correctement, voyons un cas pratique...

  • L'utilisateur crée un rectangle r et carré c.
  • Il décide de leur affecter une largeur égale à 2. Le carré, sachant que ses 4 côtés sont égaux, en profite pour affecter une hauteur identique.
  • Il décide ensuite de leur affecter une hauteur égale à 3. Le carré, sachant que ses 4 côtés sont égaux, en profite pour affecter une largeur identique.
  • L'utilisateur demande ensuite la surface du rectangle, et la méthode lui renvoie 6, ce qui est correct.
  • Il demande ensuite la surface du carré, et selon Liskov le résultat attendu est identique à celui du rectangle (6). Cependant, le résultat pour le carré est différent (9).

Que s'est-il passé ? La méthode donne surface s'est comportée exactement de la méme manière pour le carré que pour le rectangle (largeur*hauteur), mais nous violons le principe de substitution de Liskov à chaque fois que nous affectons une valeur pour la largeur ou la hauteur du carré, car cela provoque un résultat différent.

Pourtant nous sommes obligés dans les affectations du carré d'afecter la méme valeur à la largeur et à la hauteur, sinon nous ne respectons plus la sémantique du carré.

Contents Haut

Héritage et substitution de Liskov

Un des moyens de respecter les principes de substitutions de Liskov est d'inverser notre conçeption habituelle de l'héritage, et de coder un rectangle qui hérite d'un carré.

Seulement je ne suis pas d'accord avec cette méthode, car elle est totalement opposée à la vision que nous avons de la relation entre un carré et un rectangle.

Proposition de solution

Nous pouvons donc utiliser la solution suivante : comme notre carré ne peut pas produire le résultat attendu sur un rectangle, nous ne définirons pas le carré comme héritant du rectangle, mais tous deux sont au même niveau, héritant du quadrilatère.

« Heu... Comment je fais si je veux mettre ensemble les carrés et les rectangles ? »

Comme le carré et le rectangle possèdent des caractéristiques communes que ne partagent pas les autres quadrilatères (4 angles droits), nous pouvons dire que le carré et le rectangle implémentent l'interface "QuatreAnglesDroits".

Par exemple en Java, si nous demandons sur notre objet qui est de type réel carré ou rectangle s'il est une instance de "Quadrilatère", il nous répondra "oui", et de la même manière si nous lui demandons s'il est une instance de "QuatreAnglesDroits" il nous répondra aussi "oui".

Contents Haut

Spécifications et substitution de Liskov

L'abstraction par spécifications nous donne quelques pistes pour respecter le principe de substitution de Liskov :

  • La clause REQUIRES dans le sous-type doit être égale ou inférieure à celle du type parent : affaiblissement des pré-conditions.
  • La clause EFFECTS dans le sous-type doit produire au moins le même résultat que celle du type parent : renforcement des post-conditions.

Remarque

Même si je n'adhère pas toujours aux propos de Barbara Liskov4, les principes de substitution de Liskov soulèvent certaines questions intéressantes, et nous sensibilisent à certains dangers dans notre pratique de l'orienté-objet.

Nous devons avoir conscience du problème, mais nous utilisons souvent les principes d'héritage sans modifier le comportement des sous-classes, ce qui est sans danger.

Je n'ai pas le temps pour l'instant de détailler plus le sujet, mais il mérite que l'on s'y attarde, et je le ferais par la suite.

Contents Haut

English translation

You have asked to visit this site in English. For now, only the interface is translated, but not all the content yet.

If you want to help me in translations, your contribution is welcome. All you need to do is register on the site, and send me a message asking me to add you to the group of translators, which will give you the opportunity to translate the pages you want. A link at the bottom of each translated page indicates that you are the translator, and has a link to your profile.

Thank you in advance.

Document created the 03/01/2010, last modified the 26/10/2018
Source of the printed document:https://www.gaudry.be/en/oriente-objet-principe-substitution.html

The infobrol is a personal site whose content is my sole responsibility. The text is available under CreativeCommons license (BY-NC-SA). More info on the terms of use and the author.

Notes

  1. a,b MIT : Massachusetts Institute of Technology

  2.  Barbara Liskov : Barbara Liskov (7-11-1939) est professeur au MIT [Massachusetts Institute of Technology] au département Engineering's Electrical Engineering and Computer Science.

  3.  Jeannette M. Wing : Jeannette M. Wing, a fait ses études au MIT [Massachusetts Institute of Technology], et est professeur d'informatique à l'Université Carnegie Mellon. Elle est directrice du département d'informatique.

  4.  Liskov et equals : Barbara Liskov considère que l'implémentation de la méthode equals retourne true, pour des objets mutables, uniquement lors d'une égalité de pointeur (l'opérateur ==). Son argumentation est tout à fait justifiée, mais sa solution n'est absolument pas acceptable, car Java se base sur cette méthode (parfois en corrélation avec hashCode) pour l'égalité de deux objets au niveau de la logique métier, par exemple dans les collections.

Contents Haut

References

  1. book Language of the document:fr La maîtrise du développement du logiciel  : abstraction et spécification : Barbara Liskov et John V. Guttag (1990)
  2. View the nbsp;document Language of the document:uk The Liskov Substitution Principle : T. S. Norvell (2003)

These references and links indicate documents consulted during the writing of this page, or which may provide additional information, but the authors of these sources can not be held responsible for the content of this page.
The author This site is solely responsible for the way in which the various concepts, and the freedoms that are taken with the reference works, are presented here. Remember that you must cross multiple source information to reduce the risk of errors.

Contents Haut