Suponha que queremos modelar diferentes tipos de comida; pizzas e tortas. Tanto pizzas quanto tortas têm um tipo de cobertura, mas suas coberturas são diferentes.
Implementamos uma classe abstrata Baked
, e duas classes concretas Pizza
e Pie
. Também implementamos uma classe abstrata Topping
, e duas classes concretas PizzaTopping
e PieTopping
.
Por definição, qualquer implementação concreta de Baked
deve ter um Topping
, então Baked
deve ter um atributo do tipo Topping
. Além disso, esse atributo de cobertura específico deve ser do tipo PizzaTopping
em Pizza
, e PieTopping
em Pie
.
Se entendi corretamente a resposta anterior , o LSP é violado nessa situação, porque se deixarmos de lado o fato de que Baked
é abstrato, seria possível instanciar um Baked
contendo a PieTopping
, mas isso não é possível se substituirmos Baked
por Pizza
.
Minha pergunta é: existe uma maneira de implementar esse modelo sem violar o LSP?
Abordarei primeiro a preocupação imediata, mas depois terei algo a dizer sobre as suposições gerais implícitas na pergunta.
Usando pseudocódigo semelhante ao C#, você poderia definir
Pizza
assim:Depois faça o mesmo para a
Pie
turma.Isso pode agora levantar a questão natural: por que ter uma
Topping
classe abstrata?De fato, com base nas informações do OP, não temos informações suficientes para responder a essa pergunta.
Topping
Define algum comportamento polimórfico que classes derivadas podem implementar? Se sim, qual é? E alguma dessas coisas se relaciona com o Princípio de Substituição de Liskov (LSP)?É lamentável que universidades, bootcamps e cursos online continuem insistindo em ensinar design orientado a objetos a partir da noção de que se trata de modelar objetos concretos no mundo real. Pode ser assim que o Simula começou, mas raramente acaba sendo a melhor maneira de estruturar código em linguagens como Java ou C#.
O LSP não é sobre estrutura, mas sobre comportamento. O entendimento moderno do LSP é realmente capturado muito bem pela entrada da Wikipedia , que discute que
Tudo isso (pré-, pós-condições e invariantes) são, no sentido da visão de OOD de Bertrand Meyer , parte do contrato de um tipo . Apenas o designer de um tipo pode dizer qual é o contrato, e como o OP não afirma isso, não podemos realmente responder se um determinado design viola ou não o LSP.
Vamos tentar analisar cuidadosamente sua pergunta.
Princípio de substituição de Liskov (LSP):
No seu caso, isso significa o seguinte: se você tiver algum código (por exemplo, um método
f
) que recebeBaked
como parâmetro, então se você passar instância dePizza
ouPie
como parâmetro para ele, ele deverá funcionar corretamente.Segue-se dessa afirmação que não podemos falar sobre conformidade com o LSP sem entender o método
f
, bem como as implementações de métodosBaked
em classesPizza
ePie
. Porque essa informação afeta diretamente o entendimento da "correção do programa".