SOLID, Os 5 Princípios da POO
O que é SOLID?
SOLID é um acrônimo criado por Michael Feathers, que representa os cinco princípios da orientação a objetos (Identificados por Robert C. Martin “Uncle Bob”). Esses princípios (boas práticas) facilitam o desenvolvimento de softwares, que podem ser aplicados em qualquer linguagem de programação orientadas a objetos, deixando-as mais fáceis de manter.
Aqui estaremos passando rapidamente por cada princípio, e futuramente iremos aprofundar em cada um deles em outros artigos. Ah, e para não esquecer estaremos utilizando os exemplos em NodeJS & TypeScript 😁
👉 SRP — Single Responsibility Principle
(Princípio da Responsabilidade Única)
Uma classe deve ter apenas uma responsabilidade.
Cada classe deve ter responsabilidade apenas sobre um único assunto. Uma classe pode ter várias propriedades e métodos onde todos eles levam a uma única responsabilidade. O benefício desse princípio é que classes menores são mais fáceis de ler, manter e testar (testes unitários), menor acoplamento e organização.
No exemplo abaixo podemos ver que a classe Person tem várias responsabilidades:
Agora o segundo exemplo, mostra como lidar com isso seguindo o Princípio da Responsabilidade Única. Onde cada classe tem a sua finalidade.
👉 OCP — Open/Close Principle
(Princípio Aberto/Fechado)
Toda classe deve ser aberta para extensão mas fechada para modificação.
Este princípio afirma que quando novos comportamentos precisam ser adicionados, devemos estender e não alterar o código original.
No exemplo abaixo, podemos ver uma maneira errada de fazer isso. Onde temos uma função que verifica qual o tipo e retorna os interesses. Agora imagina se precisarmos incluir mais alguns tipos, precisaríamos alterar essa função a cada tipo novo.
Então, refatorando esse código, utilizando o Princípio de Aberto/Fechado, adicionamos uma classe abstrata (também poderia ser uma interface), para que as outras classes possam estendê-la.
👉 LSP — Liskov Substitution Principle
(Princípio da Substituição de Liskov)
Subclasses podem ser substituídas por sua classe pai.
Este princípio foi introduzido por Barbara Liskov em 1987, durante seu discurso na conferência “Data Abstraction”. Ela afirma que os objetos de uma superclasse devem ser substituíveis por suas subclasses, o que significa que os tipos derivados devem ser completamente substituíveis por seus tipos básicos.
se S é um subtipo de T, então os objetos do tipo T podem ser substituídos pelos objetos de tipo S. Wikipedia.
Abaixo temos um exemplo de bom uso do princípio da Substituição de Liskov. Separando as interfaces.
👉 ISP — Interface Segregation Principle
(Princípio da Segregação da Interface)
Uma classe não é obrigada a implementar interfaces e métodos que ela não irá utilizar.
Esse princípio basicamente fala que é melhor criar interfaces mais específicas ao invés de termos uma única interface genérica. Quando o software cresce, eventualmente surgem novos requisitos, e isso acaba sendo necessário adicionar mais funcionalidades, ao invés de criar uma interface maior, podemos dividi-las em interfaces menores, para garantir que as classes que a irão implementar só utilizem e implementem o que for necessário.
Aqui nesse exemplo, temos a interface Printer, que imprime e grampeia, e a Classe HPPrinter que esta implementando as duas erroneamente, pois essa impressora não pode grampear.
Então para seguir o Princípio da Segregação da Interface dividimos seus recursos em duas interfaces, e implementamos apenas nas classes que precisam delas.
👉 DIP — Dependency Inversion Principle
(Princípio da Inversão de Dependência)
Dependa de abstrações e não implementações.
Esse princípio, segundo Uncle Bob, denota 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 também que “Abstrações não devem depender de detalhes, detalhes devem depender de abstrações.”
O principal objetivo desse princípio é reduzir acoplamento, que também pode ser alcançado com outros 2 princípios — OCP e LSP.
Aqui, temos um exemplo de conexão com o banco de dados. Porém se precisarmos incluir mais tipos de bancos além deste do exemplo abaixo?
Para resolver isso, criamos uma interface Database. E uma classe para cada tipo de banco que precisarmos. Assim a classe ShapeManager nao precisa saber qual o tipo de banco estará utilizando. Dependendo apenas da interface.
⚠️ Fique atento! Inversão de Dependência (Princípio) não é a mesma coisa que Injeção de Dependência (Design Pattern).
👎 O que acontece se eu não aplicar esses princípios? Quais problemas eu posso ter?
- Código sem reaproveitamento, uma classe ou funcionalidade que não pode ser reaproveitada em outra parte do código;
- Código repetido, uma mesma parte do código em várias partes do sistema;
- Código com baixa coesão e alto acoplamento;
- Dificuldade em realizar testes unitários;
- Sistema frágil ou rígido, qualquer mudança provoca várias falhas e dificuldade na manutenção;
👍 E agora se você utilizar o SOLID, veja os benefícios você irá adquirir
- Reaproveitamento do código;
- Fácil de testar;
- Quando houver mudanças, será fácil de adaptar;
- Código aberto para receber mudanças e melhorias sem problemas de falhas.
- Código fácil de manutenção e entendimento;
Por fim…
Além dos princípios do SOLID, também existem outras técnicas de boas práticas nos auxiliam e muito no desenvolvimento, e acredito que aplicando elas hoje irá facilitar e muito futuramente, escrevendo códigos mais limpos e mais estruturados.