Comprendo correttamente che il principio di sostituzione di Liskov non può essere osservato nelle lingue in cui gli oggetti possono ispezionarsi, come è normale nelle lingue tipizzate con l'anatra?
Ad esempio, in Ruby, se una classe B
eredita da una classe A
, allora per ogni oggetto x
di A
, x.class
tornerà A
, ma se x
è un oggetto di B
, x.class
non tornerà A
.
Ecco una dichiarazione di LSP:
Let q (x) sia una dimostrabile proprietà sugli oggetti x di tipo T . Poi q (y) deve essere dimostrabile per oggetti y di tipo S dove S è un sottotipo di T .
Quindi in Ruby, ad esempio,
class T; end
class S < T; end
violare LSP in questo modulo, come testimoniato dalla proprietà q (x) =x.class.name == 'T'
Addizione. Se la risposta è "sì" (LSP incompatibile con l'introspezione), allora la mia altra domanda sarebbe: c'è qualche forma modificata di LSP "debole" che può eventualmente valere per un linguaggio dinamico, possibilmente in alcune condizioni aggiuntive e solo con tipi speciali di proprietà .
Aggiornare. Per riferimento, ecco un'altra formulazione di LSP che ho trovato sul web:
Le funzioni che utilizzano puntatori o riferimenti a classi di base devono essere in grado di utilizzare oggetti di classi derivate senza saperlo.
E un altro:
Se S è un sottotipo dichiarato di T, gli oggetti di tipo S devono comportarsi come dovrebbero comportarsi oggetti di tipo T, se trattati come oggetti di tipo T.
L'ultimo è annotato con:
Si noti che l'SPL riguarda il comportamento previsto degli oggetti. Si può seguire l'LSP solo se si è chiari su quale sia il comportamento previsto degli oggetti.
Questo sembra essere più debole di quello originale e potrebbe essere possibile osservarlo, ma vorrei vederlo formalizzato, in particolare spiegato chi decide quale sia il comportamento previsto.
Quindi LSP non è una proprietà di una coppia di classi in un linguaggio di programmazione, ma di una coppia di classi insieme a un determinato insieme di proprietà, soddisfatte dalla classe antenata? In pratica, ciò significherebbe che per costruire una sottoclasse (classe discendente) rispettando LSP, tutti i possibili usi della classe antenata devono essere conosciuti? Secondo LSP, la classe degli antenati dovrebbe essere sostituibile con qualsiasi classe discendente, giusto?
Aggiornare. Ho già accettato la risposta, ma vorrei aggiungere un altro esempio concreto di Ruby per illustrare la domanda. In Ruby, ogni classe è un modulo nel senso che la Class
classe è una discendente della Module
classe. Tuttavia:
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)