Movatterモバイル変換


[0]ホーム

URL:


Skip to content
DEV Community
Log in Create account

DEV Community

Cover image for LSP - O Princípio da Substituição de Liskov
Thiago Souza
Thiago Souza

Posted on • Edited on

     

LSP - O Princípio da Substituição de Liskov

OLSP (Liskov Substitution Principle) é mais um dos 5Princípios SOLID que todo bom programador e/ou arquiteto de software deveria conhecer. No entanto requer uma atenção especial pois ele é abstrato e mais difícil de entender, principalmente para quem está começando com SOLID.

Definição do LSP

Barbara Liskov, em maio de 1988, escreveu o seguinte texto para definir subtipos:

Se, para cada objeto o1 de tipo S, houver um objeto o2 de tipo T, de modo que, para todos os programas de tipo P não seja modificado quando o1 for substituído por o2, então S é um subtipo de T.

Nazaré Confusa

Sim! O nó no cérebro acontece de forma natural quando você tem o primeiro contato com esta definição. Mas não se desespere pois o texto escrito porLiskov pode ser simplificado e entendido como:

Sempre que o programa esperar receber uma instância de um tipoT, um subtipoS derivado deT deve poder substituí-lo sem que o programa precise de qualquer adequação para atender uma instância do subtipoS. Caso contrário,S não deveria ser um subtipo derivado deT.

Importante: ⚠️

👉 O LSP não se trata apenas de uma orientação sobre o uso da herança. Com o passar dos anos, o LSP se transformou em um princípio de design de software aplicável a qualquer tipo de interface e implementação.

👉 De forma mais abrangente, o segredo do sucesso épreservar as expectativas e garantir que ocomportamento dos subtipos sejacompatível com a intenção do tipo base.

Fechou? Agora podemos continuar e você já pode tirar um print desta informação e fazer um belo papel de parede! 😊

Violação do LSP

Montei um exemplo em.NET C# (simples e para fins didáticos) que deve ajudar a entenderuma das formas de violar o Princípio da Substituição de Liskov.

Observe com calma cada uma das classes:

publicclassVehicle{publicboolIsIgnitionOn{get;protectedset;}publicboolIsMoving{get;privateset;}publicvirtualvoidStartIgnition(){this.IsIgnitionOn=true;}publicvoidStartMove(){this.IsMoving=true;}}publicclassCar:Vehicle{publicoverridevoidStartIgnition(){this.IsIgnitionOn=true;}}publicclassMotorcycle:Vehicle{publicoverridevoidStartIgnition(){this.IsIgnitionOn=true;}}publicclassBicycle:Vehicle{publicoverridevoidStartIgnition(){thrownewNotImplementedException();}}
Enter fullscreen modeExit fullscreen mode

Agora vamos ver o que acontece quando executamos o métodoStartIgnition para diferentes instâncias deVehicle:

publicvoidStartVehicleIgnition(Vehiclevehicle){vehicle.StartIgnition();if(vehicle.IsIgnitionOn){Console.WriteLine($"O veículo está com a ignição ligada.");}}StartVehicleIgnition(newVehicle());// ✅ O veículo está com a ignição ligada.StartVehicleIgnition(newCar());// ✅ O veículo está com a ignição ligada.StartVehicleIgnition(newMotorcycle());// ✅ O veículo está com a ignição ligada.StartVehicleIgnition(newBicycle());// ❌ NotImplementedException
Enter fullscreen modeExit fullscreen mode

Neste caso fica evidente que, apesar de a bicicleta ser um veículo, no escopo desteprograma, uma instância deBicycle não consegue se adequar ao métodoStartIgnition e muito menos à propriedadeIsIgnitionOn que representa um estado interno herdado da classe baseVehicle.

Alterar o programa, incluindo um tratamento de erro para atender única e exclusivamente uma ou mais instâncias deBicycle, deveria ser considerado um crime.

Mas como eu sempre digo: Você não está aqui por acaso! A entropia do universo, de uma forma singular, te trouxe até aqui e agora você terá um bom exemplo para compartilhar com os seus colegas de trabalho para que eles também não cometam mais esse tipo erro.

Aplicação do LSP

Para corrigir e evitar que problemas como estes ocorram, poderiamos seguir com a seguinte abordagem:

publicclassVehicle{publicboolIsMoving{get;privateset;}publicvoidStartMove(){this.IsMoving=true;}}publicinterfaceIVehicleMotorized{decimalIsIgnitionOn{get;}voidStartIgnition();}publicclassCar:Vehicle,IVehicleMotorized{publicdecimalIsIgnitionOn{get;privateset;}publicvoidStartIgnition(){this.IsIgnitionOn=true;}}publicclassMotorcycle:Vehicle,IVehicleMotorized{publicdecimalIsIgnitionOn{get;privateset;}publicvoidStartIgnition(){this.IsIgnitionOn=true;}}publicclassBicycle:Vehicle{// Atributos e métodos específicos para bicicletas.}
Enter fullscreen modeExit fullscreen mode

Neste caso criamos uma interface denominadaIVehicleMotorized e fizemos as classesCar eMotorcycle implementarem as propriedades e comportamentos da nossa nova abstração.

Quanto à classeVehicle, removemos tudo o que não pode ser utilizado em bicicletas e deixamos apenas comportamentos/métodos que estejam diretamente relacionados com o que chamamos de veículos no escopo deste programa.

Vamos refatorar o trecho onde utilizamos o métodoStartIgnition e a propriedadeIsIgnitionOn para garantir que apenas classes que implementem a interfaceIVehicleMotorized possam ser acionadas no métodoStartVehicleIgnition:

publicvoidStartVehicleIgnition(IVehicleMotorizedvehicle){vehicle.StartIgnition();if(vehicle.IsIgnitionOn){Console.WriteLine($"O veículo está com a ignição ligada.");}}StartVehicleIgnition(newCar());// ✅ O veículo está com a ignição ligada.StartVehicleIgnition(newMotorcycle());// ✅ O veículo está com a ignição ligada.
Enter fullscreen modeExit fullscreen mode

Para concluir, vamos criar um programa de exemplo onde nenhum comportamento inesperado acontece ao realizar a substituição da classe baseVehicle por qualquer uma das outras classes derivadas:

publicvoidStartMoveVehicle(Vehiclevehicle){vehicle.StartMove();if(vehicle.IsMoving){Console.WriteLine("O veículo está em movimento.");}}StartMoveVehicle(newVehicle());// ✅ O veículo está em movimento.StartMoveVehicle(newCar());// ✅ O veículo está em movimento.StartMoveVehicle(newMotorcycle());// ✅ O veículo está em movimento.StartMoveVehicle(newBicycle());// ✅ O veículo está em movimento.
Enter fullscreen modeExit fullscreen mode

Finalizamos por aqui! 😊

Como mencionei anteriormente, este foi um exemplo deuma dentreoutras formas de violação deste princípio. Esta discussão pode ser bem extensa e acho que merece um vídeo no futuro. 👀

Antes de ir embora, me diz aí:você já cometeu ou testemunhou algum tipo de violação do LSP?


Obrigado pela sua atenção e espero que este artigo tenha sido útil para você.

Me siga para receber mais conteúdos como este. ❤️


Recomendação de leitura:
Arquitetura Limpa - O Guia do Artesão para Estrutura e Design de Software
Robert Cecil Martin, 2019.

Top comments(0)

Subscribe
pic
Create template

Templates let you quickly answer FAQs or store snippets for re-use.

Dismiss

Are you sure you want to hide this comment? It will become hidden in your post, but will still be visible via the comment'spermalink.

For further actions, you may consider blocking this person and/orreporting abuse

Desenvolvedor de Software com mais de 10 anos de experiência. Gosto muito de estudar e me manter o mais atualizado possível. É um prazer poder compartilhar contigo um pouco do que aprendi até aqui. 🤘
  • Location
    Londrina, Brasil
  • Work
    Desenvolvedor de Software / Líder Técnico @ Golfleet
  • Joined

More fromThiago Souza

DEV Community

We're a place where coders share, stay up-to-date and grow their careers.

Log in Create account

[8]ページ先頭

©2009-2025 Movatter.jp