LSP est nécessaire lorsqu'un code pense qu'il appelle les méthodes d'un type T
et peut, sans le savoir, appeler les méthodes d'un type S
, donde S extends T
(c'est-à-dire S
hérite, dérive ou est un sous-type du supertype T
).
Par exemple, cela se produit lorsqu'une fonction avec un paramètre d'entrée de type T
est appelé (c'est-à-dire invoqué) avec une valeur d'argument de type S
. Ou, lorsqu'un identifiant de type T
on lui attribue une valeur de type S
.
val id : T = new S() // id thinks it's a T, but is a S
LSP exige les attentes (c'est-à-dire les invariants) pour les méthodes de type T
(par exemple Rectangle
), ne soit pas violée lorsque les méthodes de type S
(par exemple Square
) sont appelés à la place.
val rect : Rectangle = new Square(5) // thinks it's a Rectangle, but is a Square
val rect2 : Rectangle = rect.setWidth(10) // height is 10, LSP violation
Même un type avec champs immuables possède encore des invariants, par exemple le immuable Les régleurs de rectangles s'attendent à ce que les dimensions soient modifiées de manière indépendante, mais la fonction immuable Les régleurs d'équerre violent cette attente.
class Rectangle( val width : Int, val height : Int )
{
def setWidth( w : Int ) = new Rectangle(w, height)
def setHeight( h : Int ) = new Rectangle(width, h)
}
class Square( val side : Int ) extends Rectangle(side, side)
{
override def setWidth( s : Int ) = new Square(s)
override def setHeight( s : Int ) = new Square(s)
}
LSP exige que chaque méthode du sous-type S
doit avoir un ou plusieurs paramètres d'entrée contravariants et une sortie covariante.
Contravariant signifie que la variance est contraire à la direction de l'héritage, c'est-à-dire le type Si
, de chaque paramètre d'entrée de chaque méthode du sous-type S
doit être le même ou un supertype du type Ti
du paramètre d'entrée correspondant de la méthode correspondante du supertype T
.
La covariance signifie que la variance est dans la même direction que l'héritage, c'est-à-dire le type So
, de la sortie de chaque méthode du sous-type S
doit être le même ou un sous-type du type To
de la sortie correspondante de la méthode correspondante de la supertype T
.
En effet, si l'appelant pense avoir un type T
pense qu'il appelle une méthode de T
alors il fournit un ou plusieurs arguments de type Ti
et assigne la sortie au type To
. Lorsqu'il appelle effectivement la méthode correspondante de S
alors chaque Ti
L'argument d'entrée est affecté à un Si
et le paramètre d'entrée So
La sortie est affectée au type To
. Ainsi, si Si
n'étaient pas contravariants par rapport à Ti
alors un sous-type Xi
-qui ne serait pas un sous-type de Si
-pourrait être attribué à Ti
.
En outre, pour les langages (par exemple, Scala ou Ceylon) qui ont des annotations de variance de site de définition sur les paramètres de polymorphisme de type (c'est-à-dire les génériques), la co- ou la contre-direction de l'annotation de variance pour chaque paramètre de type du type T
doit être en face de ou la même direction respectivement à chaque paramètre d'entrée ou sortie (de chaque méthode de T
) qui a le type du paramètre de type.
En outre, pour chaque paramètre d'entrée ou de sortie qui a un type de fonction, la direction de la variance requise est inversée. Cette règle est appliquée de manière récursive.
Le sous-typage est approprié où les invariants peuvent être énumérés.
De nombreuses recherches sont en cours sur la manière de modéliser les invariants, afin qu'ils soient appliqués par le compilateur.
Typestate (voir page 3) déclare et applique des invariants d'état orthogonaux au type. Alternativement, les invariants peuvent être appliqués par convertir les assertions en types . Par exemple, pour affirmer qu'un fichier est ouvert avant de le fermer, alors File.open() pourrait retourner un type OpenFile, qui contient une méthode close() qui n'est pas disponible dans File. A API de type "tic-tac-toe peut être un autre exemple d'utilisation du typage pour faire respecter les invariants au moment de la compilation. Le système de type peut même être Turing-complet, par ex. Scala . Les langages à typage dépendant et les prouveurs de théorèmes formalisent les modèles de typage d'ordre supérieur.
En raison du besoin de sémantique pour abstraction par rapport à l'extension Je m'attends à ce que l'emploi du typage pour modéliser les invariants, c'est-à-dire une sémantique dénotationnelle d'ordre supérieur unifiée, soit supérieur au Typestate. L'"extension" signifie la composition illimitée et permutée d'un développement modulaire et non coordonné. Parce que cela me semble être l'antithèse de l'unification et donc des degrés de liberté, d'avoir deux modèles mutuellement dépendants (par exemple, les types et le Typestate) pour exprimer la sémantique partagée, qui ne peut pas être unifié avec l'autre pour la composition extensible. Par exemple, Problème d'expression a été unifié dans les domaines du sous-typage, de la surcharge de fonctions et du typage paramétrique.
Ma position théorique est que pour la connaissance pour exister (voir section "La centralisation est aveugle et impropre"), il y aura jamais être un modèle général qui peut imposer une couverture à 100% de tous les invariants possibles dans un langage informatique complet de Turing. Pour que la connaissance existe, des possibilités inattendues doivent exister, c'est-à-dire que le désordre et l'entropie doivent toujours augmenter. C'est la force entropique. Prouver tous les calculs possibles d'une extension potentielle, c'est calculer a priori toutes les extensions possibles.
C'est pourquoi le théorème de la halte existe, c'est-à-dire qu'il est indécidable de savoir si chaque programme possible dans un langage de programmation complet de Turing se termine. Il est possible de prouver qu'un programme spécifique se termine (un programme dont toutes les possibilités ont été définies et calculées). Mais il est impossible de prouver que toutes les extensions possibles de ce programme se terminent, à moins que les possibilités d'extension de ce programme ne soient pas complètes en Turing (par exemple, via un typage dépendant). Puisque l'exigence fondamentale pour la complétude de Turing est la suivante récursion sans limite il est intuitif de comprendre comment les théorèmes d'incomplétude de Gödel et le paradoxe de Russell s'appliquent à l'extension.
Une interprétation de ces théorèmes les intègre dans une compréhension conceptuelle généralisée de la force entropique :
-
Théorèmes d'incomplétude de Gödel : toute théorie formelle, dans laquelle toutes les vérités arithmétiques peuvent être prouvées, est inconsistante.
-
Le paradoxe de Russell Chaque règle d'appartenance d'un ensemble qui peut contenir un ensemble, énumère le type spécifique de chaque membre ou se contient lui-même. Ainsi, soit les ensembles ne peuvent pas être étendus, soit ils constituent une récursion non limitée. Par exemple, l'ensemble de tout ce qui n'est pas une théière, s'inclut lui-même, qui s'inclut lui-même, qui s'inclut lui-même, etc .. Ainsi, une règle est incohérente si elle (peut contenir un ensemble et) n'énumère pas les types spécifiques (c'est-à-dire qu'elle autorise tous les types non spécifiés) et ne permet pas une extension non limitée. Il s'agit de l'ensemble des ensembles qui ne sont pas membres d'eux-mêmes. Cette incapacité à être à la fois cohérent et complètement énuméré sur toutes les extensions possibles, est le théorème d'incomplétude de Gödel.
-
Principe de substitution de Liskov : en général, c'est un problème indécidable de savoir si un ensemble est le sous-ensemble d'un autre, c'est-à-dire que l'héritage est généralement indécidable.
-
Référencement Linsky il est indécidable de savoir quel est le calcul de quelque chose, lorsqu'il est décrit ou perçu, c'est-à-dire que la perception (la réalité) n'a pas de point de référence absolu.
-
Le théorème de Coase Il n'y a pas de point de référence externe, donc toute barrière aux possibilités externes illimitées est vouée à l'échec.
-
Deuxième loi de la thermodynamique : l'univers entier (un système fermé, c'est-à-dire tout) tend vers le désordre maximal, c'est-à-dire le maximum de possibilités indépendantes.
0 votes
Autres exemples d'adhésion et de violation des FSL aquí
1 votes
Cette question a une infinité de bonnes réponses et est donc trop vaste .
0 votes
12 ans plus tard, il serait formidable que vous puissiez annuler votre propre réponse et en accepter une autre, puisque votre propre réponse est malheureusement la suivante complètement faux et ne décrit pas un LSP mais plutôt une erreur d'interface. Et, depuis qu'elle est acceptée, elle induit beaucoup de gens en erreur (comme l'indique le nombre de votes positifs).
3 votes
Cela fonctionne pour moi. Je suis passé à la réponse la plus populaire.
0 votes
Nombre mille upvote !
0 votes
C'est l'un des meilleurs exemples que j'ai trouvés : baeldung.com/java-liskov-substitution-principe
0 votes
Le principe de substitution de Liskov stipule que les sous-classes doivent être aveuglément substituables à leurs classes de base.
0 votes
Le principe de substitution de Liskov stipule que les sous-classes doivent être aveuglément substituables à leurs classes de base.
0 votes
Le principe de substitution de Liskov stipule que les sous-classes doivent être aveuglément substituables à leurs classes de base.