Solid: 5 boas práticas para o desenvolvimento de software

Conhecer o conceito de SOLID e o que cada uma das letras do acrônimo significa torna clara sua importância para a construção de um código que siga as boas práticas de desenvolvimento de software.

Um dos acrônimos mais conhecidos no mundo do desenvolvimento de software é o Solid. Se não é, pelo menos deveria ser. Este termo foi criado por Robert C. Martin, no qual ele juntou várias das boas práticas mais utilizadas referentes ao design de código. São poucas letras que contém muito significado.

Nesse acrônimo, estão reunidas 5 boas práticas para o desenvolvimento de software. Segui-las nos dará uma boa noção sobre como está nosso código e o quão fácil será a manutenção desse sistema. Ele também nos ajuda a obter respostas, como por exemplo, saber se nossa classe está com muitas responsabilidades, ou mesmo se a herança está estruturada da melhor maneira possível.

Abaixo irei explicar o que cada uma dessas letras significa:

1. Single Responsibility Principle (SRP)

Esse padrão diz que uma classe só deve ter um único motivo para mudar. Apesar de parecer simples, pode confundir. Geralmente, uma dica é sempre pensar em casos de uso. Uma responsabilidade seria um caso de uso do projeto onde estamos trabalhando. Normalmente, é bom pensar em regras de negócio, como por exemplo, o que torna um produto válido, se tem campos nulos etc.

2. Open Closed Principle (OCP)

Uma classe deve ser aberta para extensão e fechada para modificação. Robert C. Martin considera esse o princípio mais importante da orientação a objetos. Esse princípio foi definido por Bertrand Meyer em 1988 em seu livro Object-Oriented Software construnction.

Para conseguirmos alcançar o que esse princípio orienta, geralmente fazemos uso da composição e/ou uso de interfaces. Imagine uma classe impressora que serve para enviar um documento para uma determinada marca de impressora, uma HP, por exemplo.

Agora foi solicitada uma modificação no projeto para que seja possível enviar o mesmo documento para uma empresa de outra marca, como uma Epson. Uma das abordagens seria adicionar uma flag com a marca da impressora, dessa forma seria feito um if para saber qual marca da impressora foi passada no argumento, para daí escolher o método apropriado.

A classe escrita dessa forma violaria esse princípio. O ideal seria criar uma interface, chamada Impressora, por exemplo, e a partir daí criar implementações dela para cada marca que vamos trabalhar. Assim, poderíamos ter uma classe chamada ‘impressoraHP’ e outra ‘ImpressoraEpson’, por exemplo. Então, na classe que precisa de uma impressora, passar a interface Impressora.

Dessa forma, sempre que for solicitado que seja adicionada uma nova marca de impressora, só precisaremos criar uma nova classe que implemente a interface Impressora e não mais modificar a implementação que faz uso dessas classes.

3. Liskov Substitution principle (LSP)

Seja Φ(x) uma propriedade demonstrável sobre objetos x do tipo T. Então Φ(y) deve ser verdadeiro para objetos y do tipo S onde S é um subtipo de T.

Sejamos honestos, essa definição científica é bem complicada de entender, e não ajuda muito a nós desenvolvedores. Então, o que isso quer dizer de fato?

O que esse princípio nos diz é o seguinte: que as subclasses devem ser utilizadas no lugar das superclasses, por uma de suas subclasses sem que haja qualquer alteração nas classes que as utilizam.

Voltemos a estrutura falada anteriormente no Open Closed Principle, a interface Impressora, que foi citada como exemplo, pode ser considerada, pois podemos passar a impressora nos lugares onde precisaremos e as subclasses vão se comportar de maneira apropriada.

Um outro exemplo, poderia ser criar uma super classe chamada Transporte, e nessa classe ter um método chamado ligarMotor(){} então podemos criar subclasses, como por exemplo, carro, ônibus, moto, etc. Pode parecer tudo certo com ela e o princípio de Liskov, mas o que aconteceria se criássemos uma subclasse bicicleta? Ela também é um meio de transporte, mas geralmente não tem motor.

4. Interface Segregation Principle (ISP)

Este princípio é definido por Robert C. Martin da seguinte maneira: “Clients should not be forced to depend upon interfaces that they do not use.” (Clientes não devem ser forçados a depender de interfaces que eles não usam, em tradução livre). Parece óbvio mas não é, o objetivo desse princípio é reduzir a complexidade referente às mudanças, dividindo o software em várias partes independentes. Esse princípio é muito ligado a outra afirmação do desenvolvimento de software, que diz, programe para uma interface e não para implementação, e avança um pouco mais nos usos das interfaces ou classes abstratas (em Java).

Como exemplo, imagine que você está desenvolvendo um sistema para e-commerce, e nesse sistema você cria uma interface ‘ProductInterface’ com dois métodos: ‘productCommon’ e ‘productCombo’. Agora, uma classe, por exemplo: Produto Comum’, deveria implementar essa interface e lançar uma ‘UnsupportedOperationException’ no método ‘productCombo’, já uma classe ‘ProdutoCombo’ implementaria a mesma interface e lançaria uma UnsupportedOperationException no método productCommon, essas implementações já estão violando o princípio ISP.

Agora imagine que foi solicitada a criação de um nova categoria de produto, que oferece descontos para clientes que tenham alguma assinatura, se continuarmos seguindo esse design, teríamos que ir na interface ‘ProductInterface’ criar um novo método, algo como ‘productForSubscribers’ ir em todas as outras classes que implementam a interface ‘ProductInterface’ e adicionar uma ‘unsupportedOperationExceptioná para o novo método criado, quebrando totalmente a compatibilidade.

Dessa forma, seria mais apropriado criarmos três interfaces, uma para cada tipo de produto, assim, não quebraríamos a compatibilidade, e no lugar de passar as implementações passaríamos sempre a interface apropriada. E sempre que um produto for criado para uma categoria nova, não precisamos alterar nada em nosso projeto, pois, os clientes já sabem o que esperar e também, sabem as operações possíveis para qualquer tipo de produto.

5. Dependency Inversion Principle (DSP)

É baseado em uma ideia simples, mas muito importante.

  • Ele diz que módulos de alto nível não devem depender de módulos de baixo nível, ambos devem depender de abstrações ; e,
  • abstrações não devem depender de detalhes, mas os detalhes devem depender das abstrações. Perceba que esse princípio não nos diz qual a direção da dependência e sim que ambos devem depender da mesma abstração.

Esse princípio também está altamente ligado a outros dois, sendo eles OCP e LSP, se sua classe está em conformidade com esses dois padrões, podemos dizer que ela praticamente já faz uso da inversão de dependência.

Para invertermos a dependência basta fazer com que nossas classes ofereçam um modo de obter as outras classes que precisamos para realizar seu trabalho, como por exemplo, uma classe ‘Service’ qualquer que precise salvar o resultado de alguma operação na base de dados, teria uma interface ‘Repository’, como propriedade e receberia uma implementação dessa interface através de seu construtor, dessa maneira, quem usar a classe ‘Service’, também deverá fornecer uma implementação da interface ‘Repository’ passando essa implementação através do construtor da classe ‘Service

Conhecer o conceito de Solid e o que cada uma das letras do acrônimo significa torna clara sua importância para a construção de um código que siga as boas praticas de desenvolvimento de software. Além disso, possibilita a construção de um software melhor, permitindo a criação de módulos com baixo acoplamento, tornando a evolução do sistema criado uma prática muito mais simples, melhorando a manutenção do sistema ao longo do seu ciclo de vida, uma vez que as responsabilidades e conexões estarão bem definidas e organizadas.

Quer receber mais conteúdos como esse gratuitamente?

Cadastre-se para receber os nossos conteúdos por e-mail.

Email registrado com sucesso
Opa! E-mail inválido, verifique se o e-mail está correto.