Entendo corretamente que o Princípio da Substituição de Liskov não pode ser observado em linguagens em que objetos podem se inspecionar, como é habitual em linguagens tipificadas por pato?
Por exemplo, em Ruby, se uma classe B
herda de uma classe A
, então para cada objeto x
de A
, x.class
retornará A
, mas se x
for um objeto de B
, x.class
não retornará A
.
Aqui está uma declaração do LSP:
Vamos q (x) ser um provable propriedade sobre objetos x do tipo T . Em seguida, q (Y) deve ser demonstrável para objectos y de tipo S , onde S é um subtipo de T .
Então, em Ruby, por exemplo,
class T; end
class S < T; end
viole o LSP neste formulário, como testemunhado pela propriedade q (x) =x.class.name == 'T'
Adição. Se a resposta for "sim" (LSP incompatível com a introspecção), minha outra pergunta seria: existe alguma forma "fraca" modificada de LSP que pode ser válida para uma linguagem dinâmica, possivelmente sob algumas condições adicionais e apenas com tipos especiais de propriedades .
Atualizar. Para referência, aqui está outra formulação de LSP que encontrei na web:
Funções que usam ponteiros ou referências a classes base devem poder usar objetos de classes derivadas sem conhecê-lo.
E outro:
Se S é um subtipo declarado de T, objetos do tipo S devem se comportar como objetos do tipo T devem se comportar, se forem tratados como objetos do tipo T.
O último é anotado com:
Observe que o LSP tem tudo a ver com o comportamento esperado dos objetos. Só se pode seguir o LSP se estiver claro sobre qual é o comportamento esperado dos objetos.
Isso parece ser mais fraco que o original e pode ser possível observar, mas eu gostaria de vê-lo formalizado, em particular explicado quem decide qual é o comportamento esperado.
O LSP não é uma propriedade de um par de classes em uma linguagem de programação, mas de um par de classes juntamente com um determinado conjunto de propriedades, satisfeito pela classe ancestral? Na prática, isso significaria que, para construir uma subclasse (classe descendente) respeitando o LSP, todos os usos possíveis da classe ancestral devem ser conhecidos? De acordo com o LSP, a classe ancestral deve ser substituída por qualquer classe descendente, certo?
Atualizar. Eu já aceitei a resposta, mas gostaria de adicionar mais um exemplo concreto de Ruby para ilustrar a pergunta. Em Ruby, cada classe é um módulo no sentido de que Class
classe é descendente de Module
classe. Contudo:
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)