SOLID: entendendo mais a fundo os 5 princípios da POO

Gabriel Jorge Pereira
6 min readMar 5, 2021

--

SOLID são 5 princípios da programação orientada a objeto que têm como objetivo deixar o seu código mais fácil de manter e de escalar. Pelo fato do SOLID ser um conjunto de conceitos, pode assim, ser aplicado em qualquer linguagem que tenha suporte a orientação a objetos.

PS: os códigos apresentados neste artigo serão em PHP, mas os conceitos podem ser aplicados a qualquer linguagem orientada a objetos.

SOLID principles

O que é?

SOLID é um acrônimo para 5 princípios de design de software, afim de deixar o seu código mais compreensível e de fácil manutenção. Já foi citado em livros como Princípios de OOD — Robert C. Martin.

Quais são eles?

Neste primeiro momento, vamos ver somente seus nomes, mas não se preocupe que mais abaixo veremos detalhadamente e com exemplos de cada um deles.

Vamos lá!

  1. S — Single Responsiblity Principle (Princípio da Responsabilidade Única);
  2. O — Open-Closed Principle (Princípio Aberto-Fechado);
  3. L — Liskov Substitution Principle (Princípio da Substituição de Liskov);
  4. I — Interface Segregation Principle (Princípio da Segregação da Interface);
  5. D — Dependency Inversion Principle (Princípio da Inversão da Dependência);

Nomeados cada um deles, vamos ao detalhes!

1. Single Responsability Principle

O princípio da responsabilidade única prega que cada classe/função deve ter uma e somente uma responsabilidade.

É comum encontrarmos classes ou funções gigantescas por aí, com centenas de linhas e responsabilidades, o que acaba tornando muito difícil a manutenção e escalabilidade, já que ficaria tudo acoplado dentro de uma única entidade.

Vamos ver um exemplo:

No exemplo acima, temos uma classe Product responsável por manipular dados, listar dados e gerar um relatório em PDF, 3 tipos de atividades de naturezas distintas.

Este bloco de código viola totalmente o SRP, podendo causar uma enorme dificuldade na hora de dar manutenção ou realizar qualquer alteração que seja.

Aplicando nosso querido Princípio de Responsabilidade Única, teremos algo parecido com o seguinte:

Dessa maneira, separamos em 3 classes, cada uma responsável por naturezas de dados distintas e cada uma com um e somente um tipo de operação, ficando muito mais organizado e fácil de se encontrar o que deseja.

Agora temos seguinte estrutura:

  • Product: responsável pela listagem dos dados;
  • ProductRepository: responsável pela manipulação dos dados, criação, alteração e afins;
  • ProductExport: responsável por gerar relatórios em diferentes formatos;

Muito massa né? Pois é, esse é apenas o primeiro…

2. Open-Closed Principle

Chegando na letra O do SOLID temos o Open-Closed Principle (ou Princípio Aberto-Fechado), mas o que isso quer dizer?

Este princípio prega que uma classe ou entidade é Aberta para extensão mas Fechada para modificação.

Um pouco confuso, não? Então vamos lá!

Quando se tratando de orientação a objetos, temos um conceito chamado herança, que é quando uma classe herda (extends) os atributos e métodos da de uma outra classe, e é exatamente para isso que uma classe que respeita o OCP deve ser aberta, para ter suas propriedades herdadas por uma outra classe qualquer, sem que seja necessário qualquer tipo de alteração, que é a parte do fechada para modificação.

Vamos a um exemplo prático:

No exemplo assim temos uma classe de notificação que possui um método para um envio de qualquer notificação que seja.

Para saber qual notificação deve ser enviada, a função recebe um parâmetro informando qual o tipo de notificação está sendo solicitada.

Imaginando que amanhã gostaríamos de enviar uma mensagem por WhatsApp, teríamos que adicionar mais um if, alterando a nossa classe base de notificação e assim ferindo o OCP.

Para adequar o nosso código a esse princípio, faremos o seguinte:

No exemplo acima, implementamos uma classe de notificação base, a qual é herdada por qualquer outro tipo de notificação, onde cada uma tem o seu próprio modelo de validação e de envio, cada uma com as suas regras e afins. Desta maneira, podemos implementar qualquer outro método de notificação sem que seja alterado nada na classe Notify, aplicando assim o OCP.

Um exemplo muito bacana desse princípio, é a maneira como os ORMs funcionam. Um ORM não tem o seu código alterado independente do banco que você estiver usando, ele interpreta e adapta os comandos para o banco que você estiver trabalhando!

2/5…vamos para o L do SOLID!

3. Liskov Substitution Principle

Um nomezinho um tanto quanto diferente né não?!

Mas é um dos mais tranquilos, chega mais!

Esse princípio prega que uma classe filha (ou seja, uma classe que foi herdada de uma outra classe) deve conseguir substituir a classe pai (a classe que teve suas propriedades e métodos herdados) sem que seja necessário nenhuma alteração.

Vejamos um exemplo:

No código acima, vemos duas classes Pessoa e JogadorDeFutebol, sendo a segunda, filha da primeira. Vemos que executando a mesma função acoesPessoa e passando tanto a classe pai quanto a classe filha como parâmetro, ela executa sem qualquer tipo de erro.

De maneira resumida, este princípio fala que os atributos e métodos implementados na classe pai, devem ser somente o comum entre todas as possíveis heranças.

ATENÇÃO!!!
Todos os métodos implementados na classe pai devem ser usados na classe filha.

O princípio estará sendo desrespeitado com métodos simplesmente declarados, sem função alguma, ou até mesmo métodos em branco.

Essa quebra também acontece com tipos diferentes de dados, as “tipagens” das classes pai e filha devem ser iguais para os seus métodos em comum.

Dito isto, vamos a penúltima letra.

4. Interface Segregation Principle

Este princípio diz que classes não podem ser forçadas a implementar métodos que ela não irá usar e que nem faz sentido para ela.

Veja um exemplo:

No exemplo acima temos uma interface Notificacao que está sendo implementada por duas outras classes, Push e Email. Além disso, possui um método carregarVideo(), porém uma push notification não pode implementar vídeos, o que geraria uma Exception.

Para uma implementação correta, o princípio fala que devemos segregar essa interface (daí vem o nome), para que ela seja específica o suficiente para não causar esse tipo de exceção.

Veja como ficaria aplicando o ISP:

Agora temos duas interfaces, uma para as notificações que suportam vídeos e outra para as que não suportam, assim, cada classe implementa a que atende a sua realidade, com a classe Email implementando a interface NotificacaoComVideo e a classe Push implementando a interface Notificacao.

Aplicando este conceito, ficará mais fácil ainda de respeitar o nosso 2º princípio, o Open-Closed Principle.

Segundo Robert C. Martin, os princípios Open-Closed e Interface Segregation quando combinados, nos levam ao nosso 5º e último princípio!

5. Dependency Inversion Principle

Por fim, da maneira mais acadêmica possível, esse princípio prega que nossas classes devem depender de abstrações e não de implementações.

Show, não entendeu nada né? Vamos lá!

Abstração nada mais é do que uma classe, sendo algo abstrato, algo que não foi implementado ainda.

Quando essa abstração passa a ser implementada, torna-se um objeto e agora temos uma implementação.

No exemplo assim, estamos trabalhando com uma classe PasswordReminder que está totalmente acoplada a uma implementação de uma classe de conexão de banco de dados, a MySQLConnection, justamente o que o Tio Bob disse para não fazermos.

Se utilizássemos dessa maneira, sempre que precisássemos usar essa classe em outro lugar, precisaríamos levar a classe MySQLConnection junto.

Partindo para um próximo passo, teríamos o seguinte:

Agora temos nossa classe menos acoplada, utilizando da Injeção de Dependência, que por sua vez não é a mesma coisa que Inversão de Dependência.

Bom, conseguimos desacoplar um pouco o nosso código, porém ainda estamos ferindo o Dependency Inversion Principle, fazendo com a nossa classe dependa de uma implementação e não de uma abstração.

Imaginando um cenário onde quiséssemos trocar o banco de dados, utilizando essa lógica de relembrar a senha, não conseguiríamos, pois nossa classe está dependendo de uma implementação de MySQL.

Partindo desse ponto e realizando mais algumas mudanças, teremos algo parecido com isso:

Agora a nossa classe está dependendo de uma abstração (não sabendo mais com qual banco de dados está lidando), algo totalmente abstrato e genérico e concordando totalmente com o nosso querido Dependency Inversion Principle ou também DIP para os mais chegados!

Conclusão

Os princípios SOLID são de suma importância em qualquer projeto que use e abuse da orientação a objetos, deixando seu código mais limpo, robusto, de fácil manutenção e escalável, contribuindo sempre para a evolução do software.

Tenho certeza que depois desse artigo sua visão para com o código irá mudar.

Já conhecia esses princípios? Comenta aí me contando um pouco mais da sua experiência, ficarei muito feliz em respondê-lo!

Primeiro contato com o SOLID? Deixa aí o que mudou quanto ao seu olhar para os códigos!

Muito obrigado pela leitura e pelo seu tempo! Se gostou, deixe um clap e compartilhe para mais pessoas conhecerem essa maravilha que é o SOLID!

Referências:

--

--

Gabriel Jorge Pereira
Gabriel Jorge Pereira

Written by Gabriel Jorge Pereira

Backend Software Engineer looking to improve my skills and bring knowledge to everyone

Responses (1)