Dois-je bien comprendre que le principe de substitution de Liskov ne peut pas être observé dans des langues où les objets peuvent s’inspecter eux-mêmes, comme ce qui est habituel dans les langues de type canard?
Par exemple, dans Ruby, si une classe B
hérite d'une classe A
, alors pour chaque objet x
de A
, x.class
va revenir A
, mais si x
est un objet de B
, x.class
ne va pas revenir A
.
Voici une déclaration de LSP:
Que q (x) soit une propriété démontrable sur les objets x de type T . Ensuite , q (y) doit être prouvable pour les objets y de type S , où S est un sous - type de T .
Ainsi, en Ruby, par exemple,
class T; end
class S < T; end
violer LSP sous cette forme, comme en témoigne la propriété q (x) =x.class.name == 'T'
Une addition. Si la réponse est "oui" (LSP incompatible avec l'introspection), alors mon autre question serait: existe-t-il une forme modifiée "faible" de LSP qui peut éventuellement tenir pour un langage dynamique, éventuellement sous certaines conditions supplémentaires et avec seulement des types spéciaux des propriétés .
Mettre à jour. Pour référence, voici une autre formulation de LSP que j'ai trouvée sur le web:
Les fonctions qui utilisent des pointeurs ou des références à des classes de base doivent pouvoir utiliser des objets de classes dérivées sans le savoir.
Et un autre:
Si S est un sous-type déclaré de T, les objets de type S devraient se comporter comme les objets de type T devraient se comporter, s'ils sont traités comme des objets de type T.
Le dernier est annoté avec:
Notez que le LSP concerne le comportement attendu des objets. On ne peut suivre le LSP que si l'on sait clairement quel est le comportement attendu des objets.
Cela semble plus faible que l'original et pourrait être possible à observer, mais j'aimerais le voir officialisé, en particulier expliqué qui décide du comportement attendu.
Le LSP n'est-il donc pas une propriété d'une paire de classes dans un langage de programmation, mais d'une paire de classes avec un ensemble donné de propriétés, satisfaites par la classe ancêtre? Concrètement, cela signifierait-il que pour construire une sous-classe (classe descendante) respectant LSP, toutes les utilisations possibles de la classe ancêtre doivent être connues? Selon LSP, la classe ancêtre est censée être remplaçable par n'importe quelle classe descendante, non?
Mettre à jour. J'ai déjà accepté la réponse, mais je voudrais ajouter un autre exemple concret de Ruby pour illustrer la question. En Ruby, chaque classe est un module dans le sens où la Class
classe est un descendant de Module
classe. Pourtant:
class C; end
C.is_a?(Module) # => true
C.class # => Class
Class.superclass # => Module
module M; end
M.class # => Module
o = Object.new
o.extend(M) # ok
o.extend(C) # => TypeError: wrong argument type Class (expected Module)