Principe de substitution de Liskov

Un article de Wikipédia, l'encyclopédie libre.
Aller à : navigation, rechercher

Le principe de substitution de Liskov (LSP) est, en programmation orientée objet, une définition particulière de la notion de sous-type. Il a été formulé par Barbara Liskov et Jeannette Wing dans un article intitulé Family Values: A Behavioral Notion of Subtyping[1] :

Liskov et Wing en ont proposé la formulation condensée suivante[2] :

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.

Principe[modifier | modifier le code]

La notion de sous-type telle que définie par Liskov et Wing est fondée sur la notion de substituabilité : si S est un sous-type de T, alors tout objet de type T peut être remplacé par un objet de type S sans altérer les propriétés désirables du programme concerné.

Le principe de Liskov impose des restrictions sur les signatures sur la définition des sous-types:

  • Contravariance des arguments de méthode dans le sous-type.
  • Covariance du type de retour dans le sous-type.
  • Aucune nouvelle exception ne doit être générée par la méthode du sous-type, sauf si celles-ci sont elle-mêmes des sous-types des exceptions levées par la méthode du supertype.

On défini également un certain nombre de conditions comportementales (voir la section Conception par contrat).

Conception par contrat[modifier | modifier le code]

Le principe de substitution de Liskov est étroitement relié à la méthodologie de programmation par contrat aboutissant à des restrictions qui spécifient la manière dont les contrats peuvent interagir avec les mécanismes d'héritage :

  • Les préconditions ne peuvent pas être renforcées dans une sous-classe. Cela signifie que vous ne pouvez pas avoir une sous-classe avec des préconditions plus fortes que celles de sa superclasse ;
  • Les postconditions ne peuvent pas être affaiblies dans une sous-classe. Cela signifie que vous ne pouvez pas avoir une sous-classe avec des postconditions plus faibles que celles de sa superclasse.

De plus, le principe de substitution de Liskov implique que des exceptions d'un type nouveau ne peuvent pas être levées par des méthodes de la sous-classe, sauf si ces exceptions sont elles-mêmes des sous-types des exceptions lancées par les méthodes de la superclasse.

Une fonction utilisant la connaissance de la hiérarchie de classe viole le principe car elle utilise une référence à la classe de base, mais doit aussi avoir connaissance des sous-classes. Une telle fonction viole le principe ouvert/fermé car elle doit être modifiée quand on crée une classe dérivée de la classe de base.

Exemple de Violation du LSP[modifier | modifier le code]

L'exemple classique d'une violation du LSP est la suivante:

  • Soit une classe Rectangle représentant les propriétés d'un rectangle: hauteur, largeur. On lui associe donc des accesseurs pour accéder et modifier la hauteur et la largeur librement. En postcondition, on défini la règle "hauteur" et "largeur" sont librement modifiable.
  • Soit une classe Carré que l'on fait dériver de la classe Rectangle. En effet, en mathématique, un carré est un rectangle. Donc, on défini naturellement la classe Carré comme sous-type de la classe Rectangle. On défini comme postcondition la règle "les 4 cotés du carré doivent être égaux".

On s'attend de pouvoir utiliser une instance de type Carré n'importe où un type Rectangle est attendu.

Problème: Un carré ayant par définition quatre cotés égaux, il convient de restreindre la modification de la hauteur et de la largeur pour qu'elles soient toujours égales. Néanmoins, si un carré est utilisé là où, comportementalement, on s'attend à interagir avec un rectangle, des comportements incohérents peuvent subvenir: Les cotés d'un carré ne peuvent être changés indépendamment, contrairement à un rectangle. Une mauvaise solution consisterait à modifier les setter du carré pour préserver l'invariance de ce dernier. Mais ceci violerait la postcondition des setter du rectangle qui spécifie que l'on puisse modifier hauteur et largeur indépendamment.

Une solution pour éviter ces incohérences est de retirer la nature Mutable des classes Carré et Rectangle. Autrement dit, elles ne sont accessibles qu'en lecture. Il n'y a aucune violation du LSP, néanmoins on devra implémenter des méthodes "hauteur" et "largeur" à un carré, ce qui, sémantiquement, est un non sens.

La solution consiste à ne pas considérer un type Carré comme substitut d'un type Rectangle, et les définir comme deux types complètement indépendent. Ceci ne contredit pas le fait qu'un carré soit un rectangle. La classe Carré est un représentant du concept "carré". La classe Rectangle est un représentant du concept "rectangle". Or, les représentants ne partagent pas les mêmes propriétés que ce qu'ils représentent[3].

Détection d'une violation LSP[modifier | modifier le code]

Un cas typique de violation du LSP peut être facilement détecté lors de l'utilisation de l'opérateur de détection du type de donnée pour conditionner un comportement en fonction du type de l'objet. Par exemple typeof en C ou C++, isinstance en Python, instanceof en Java.

Par exemple dans la fonction Python:

def aFunction(anObject):
    if isinstance(anObject, AType):
        # do something
    elif isinstance(anObject, AnotherType):
        # do something else
    else:
        # default actions for the other case

L'utilisation de cette instruction isinstance dans un module extérieur à la classe anObject est typiquement une violation du Principe de Substitution de Liskov, et devrait être remplacée par l'exploitation du polymorphisme dans le langage.

Voir aussi[modifier | modifier le code]

Notes et références[modifier | modifier le code]

  1. Barbara Liskov, Wing, Jeannette, « Family Values: A Behavioral Notion of Subtyping »,‎ 16 juillet 1993 (consulté le 5 octobre 2006).
  2. Barbara Liskov, Wing, Jeannette, « Behavioral Subtyping Using Invariants and Constraints »,‎ Juillet 1999 (consulté le 5 octobre 2006).
  3. « Episode 11, The Liskov Substitution Principle - Part 1 »

Articles connexes[modifier | modifier le code]