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.

Table des matières 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

Table des matières 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é.

Table des matières 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".

Table des matières 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.

Document créé le 03/01/2010, dernière modification le 26/10/2018
Source du document imprimé : https://www.gaudry.be/oriente-objet-principe-substitution.html

L'infobrol est un site personnel dont le contenu n'engage que moi. Le texte est mis à disposition sous licence CreativeCommons(BY-NC-SA). Plus d'info sur les conditions d'utilisation et sur l'auteur.

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.

Table des matières Haut

Références

  1. livre Langue du document :fr La maîtrise du développement du logiciel  : abstraction et spécification : Barbara Liskov et John V. Guttag (1990)
  2. Consulter le document pdf Langue du document :uk The Liskov Substitution Principle : T. S. Norvell (2003)

Ces références et liens indiquent des documents consultés lors de la rédaction de cette page, ou qui peuvent apporter un complément d'information, mais les auteurs de ces sources ne peuvent être tenus responsables du contenu de cette page.
L'auteur de ce site est seul responsable de la manière dont sont présentés ici les différents concepts, et des libertés qui sont prises avec les ouvrages de référence. N'oubliez pas que vous devez croiser les informations de sources multiples afin de diminuer les risques d'erreurs.

Table des matières Haut