
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.
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();}}
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
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.}
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.
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.
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)
For further actions, you may consider blocking this person and/orreporting abuse