Todo dev que se preocupa com qualidade de software deve saber sobre SOLID. Mas o que significa esse termo?

Trata-se de alguns princípios da programação orientada a objetos e design de códigos que facilitam no desenvolvimento de software, tornando-os mais fáceis de compreender e manter. 

Criado por Roberto C. Martin, popularmente conhecido na comunidade dev como Uncle Bob por volta dos anos 2000, o acrônimo SOLID foi introduzido por Michael Feathers, após observar que os cinco princípios poderiam se encaixar nessas palavras.

Vamos aos seus principios:

(S)ingle Responsibility Principle (Princípio da Responsabilidade Única)

(O)pen/Closed Principle (Princípio do Aberto/Fechado)

(L)iskov Substitution Principle (Princípio da Substituição de Liskov)

(I)nterface Segregation Principle (Princípio da Segregação de Interfaces)

(D)ependency Inversion Principle (Princípio da Inversão de Dependências)

Confira as melhores dicas do nosso Builder André Zarlenga sobre SOLID e seus cinco princípios de programação

Motivação

Percebemos ao longo de nossa carreira como desenvolvedores que devemos fazer um paralelo entre regra de negócio do mundo real vs implementação. Aproximar a camada de negócio, para deixar o código cada vez mais semântico. 

Eis uma palavra importante em nosso meio: Semântica. Dentro da nossa jornada de trabalho, nos deparamos com códigos que fazem sentido estarem interligados com o alvo do sistema e não somente para o sistema; nesse aspecto o desenvolvedor já não consegue ter tanta certeza do que deve entregar ou o que determinado código realiza. 

Afinal de contas, o usuário final não enxerga como é feito, ele deseja somente visualizar o resultado final e para nos aproximarmos desse alvo, devemos manter isso sempre em mente. O SOLID são princípios que nos ajudam a definir e categorizar isso.

Single Responsibility Principle (Princípio da Responsabilidade Única)

Este princípio é essencial, sem ele, não podemos avançar para os demais. Essa classe deve ter responsabilidade única, ou seja, deve tratar apenas sobre si, tendo uma única tarefa para execução.

Uma analogia que gosto de fazer para explicar como ela funciona é a relação da pessoa com seu documento. Como pessoa física, você deve ter conhecimento sobre seu dados através do nome, data de nascimento, cpf, etc. 

Perceba que se você tem conhecimento do número do seu CPF, entretanto não sabe como este é gerado e mantido, consideramos que essa tarefa é protegida por um órgão que não é de sua responsabilidade, logo não temos por que ter conhecimento dos processos para gerar esse documento. Nos limitados apenas em fazer uso do CPF onde e quando for requisitado.

As classes que violam este princípio são mais difíceis de serem mantidas. Por exemplo, se temos uma classe que mudamos muito por diferentes razões, ela deve ser dividida em múltiplas classes, cada uma tratando de uma única preocupação. Se ocorrer algum tipo de erro, será mais fácil de encontrá-lo e consequentemente solucioná-lo. 

Open/Closed Principle (Princípio do Aberto/Fechado)

Partindo do princípio que uma entrega onde tudo está funcionando ( considerando que esteja sendo aplicado o princípio anterior) é concebida sob os pilares de boas práticas de desenvolvimento de software. 

Não podemos, em hipótese alguma, inflar uma implementação contando com um único core de execução, portanto faremos isso através do fundamento de novos requisitos, evitando mexer no que já está funcionando, apenas estendendo-o ao invés de modificá-lo.

A partir de agora vale fazer uma analogia com polimorfismo; imagine que pediram a você uma flor, a qual você pode escolher conforme o pedido. O solicitante da flor pode reclamar do que você irá lhe entregar?

Se for uma flor, não há por que reclamar, afinal esta faz parte parte da abstração com diversas variações de flores tais como: rosa, petúnia, margarida, etc. 

A melhor maneira de utilizarmos uma herança em POO ( Programação Orientada a Objeto) é utilizando o polimorfismo com métodos que têm formas distintas de definição, adicionando novos tipos em nossos projetos sem quebrar o que está realizado.  A grande vantagem disso é que esses testes unitários manteriam-se, sem a necessidade de alteração por conta do acoplamento.

Liskov Substitution Principle (Princípio da Substituição de Liskov)

Este princípio básico diz: Uma classe base deve ser substituída por classes derivadas, 

perceba que o princípio anterior faz com que você trabalhe neste também. Então qual é o adendo neste caso?

O adendo refere-se ao cuidado sobre como devemos utilizar o polimorfismo, com o objetivo de não derivar etapa que não tem sentido. Fazendo referência a analogia do princípio anterior, sobre flores, deduzimos que uma flor tem características,  por exemplo: adulto apropriado para cada flor ?

Agora faço uma pergunta:  podemos colocar na família de flores, uma flor artificial?

Até faria sentido inicialmente, porém quando olhamos para a característica “adulto apropriado” vemos que tem algo errado, afinal esta, flor artificial, não tem necessidade de adubo.

Na prática deveríamos ignorar essa característica de artificialidade, aí o problema é resolvido. Porém é uma violação do princípio, onde subscrevemos um comportamento para resolver outro problema.

Interface Segregation Principle (Princípio da Segregação de Interfaces)

Neste princípio vemos problemas com implementações infladas, onde são definidos métodos através de interfaces, que nem sempre são necessários. Uma abstração de repositório a qual  consideramos que terá as operações básicas de CRUD, pode em determinado contexto, necessitar apenas de uma leitura para uma relatório.

Passei por uma situação onde o sistema que utilizávamos para implementações genéricas em diversas camadas, inclusive para uso de classes de domínio; em determinado momento desencadeou um problema com nosso framework de injeção de dependência. Por usarmos tipos genéricos com parâmetros de classe e depois que tivemos mais de um cenário para classe genérica, o framework simplesmente não sabia qual implementação injetar (o problema era um pouco mais profundo que isso), mas citei esse problema para dizer a seguinte frase:

 “Uma interface especifica, é melhor do que o contrário”

Defina sua interface mais específica para ter um objetivo bem definido.

Dependency Inversion Principle (Princípio da Inversão de Dependências)

Por último, mas não menos importante, o princípio de inversão de dependência (DIP) afirma que os módulos de alto nível não devem depender dos módulos de baixo nível; ambos devem depender de abstrações, ou seja, este nos ajuda a reduzir o acoplamento, onde vamos trabalhar com os contratos que a interface nos proporciona, e não sua implementação.

“Programe para interface, não para implementação”

Ter acoplamento zero é impossível, mas minimizamos o uso dependente, afinal trabalhamos com a definição da interface. Como isso será executado não é responsabilidade de quem faz uso deste.

Conclusão

O SOLID é requisito para começar a trabalhar com classes de qualidade, facilitando a manutenção e fazendo repensar toda a estrutura do seu código, tanto para modelar, quanto para testar. Eu levo como motivação o valor que o SOLID gera na aplicação, você pode gastar mais tempo, porém a qualidade e a manutenção resultam em melhores benefícios.