Day #01 - Os Princípios SOLID: A Base para um Código Sustentável e Manutenável
- #Arquitetura de Sistemas
- #Arquitetura
Os Princípios SOLID: A Base para um Código Sustentável e Manutenável
Antes de começar quero dizer que estou estudando para ser um programador cada vez melhor, todos os didas o que estudar e revisar, estarei aqui documentando para vocês também acompanharem e crescerem juntos. Bora lá? Quem quiser acompanhar e estudar comigo, deixa um comentário.
O desenvolvimento de software exige boas práticas para garantir um código limpo, flexível e de fácil manutenção. Nesse contexto, os princípios SOLID são fundamentais para a criação de sistemas robustos e escaláveis. SOLID é um acrônimo que representa cinco princípios essenciais da programação orientada a objetos, definidos por Robert C. Martin (Uncle Bob).
1. Single Responsibility Principle (SRP) - Princípio da Responsabilidade Única
Cada classe deve ter apenas um motivo para mudar. Isso significa que uma classe deve ter uma única responsabilidade bem definida.
Exemplo:
Errado:
public class RelatorioService {
public void gerarRelatorio() { /* Gera o relatório */ }
public void salvarNoBanco() { /* Salva no banco de dados */ }
}
Correto:
public class RelatorioGerador {
public void gerarRelatorio() { /* Gera o relatório */ }
}
public class RelatorioRepositorio {
public void salvarNoBanco() { /* Salva no banco de dados */ }
}
Ao separar as responsabilidades, evitamos acoplamento excessivo e tornamos o código mais modular.
2. Open/Closed Principle (OCP) - Princípio Aberto/Fechado
Os módulos de software devem estar abertos para extensão, mas fechados para modificação. Isso significa que devemos projetar classes que possam ser estendidas sem alterar seu código original.
Exemplo:
Errado:
public class CalculadoraDesconto {
public double calcularDesconto(String tipo, double valor) {
if ("BLACK_FRIDAY".equals(tipo)) {
return valor * 0.5;
} else if ("NATAL".equals(tipo)) {
return valor * 0.3;
}
return valor;
}
}
Correto:
public interface Desconto {
double aplicar(double valor);
}
public class BlackFridayDesconto implements Desconto {
public double aplicar(double valor) { return valor * 0.5; }
}
public class NatalDesconto implements Desconto {
public double aplicar(double valor) { return valor * 0.3; }
}
public class CalculadoraDesconto {
public double calcular(Desconto desconto, double valor) {
return desconto.aplicar(valor);
}
}
Dessa forma, ao adicionar novos tipos de descontos, não precisamos modificar a classe principal.
3. Liskov Substitution Principle (LSP) - Princípio da Substituição de Liskov
Uma classe derivada deve poder ser usada no lugar de sua classe base sem afetar a corretude do programa.
Exemplo:
Errado:
public class Ave {
public void voar() { /* Implementação de voo */ }
}
public class Pinguim extends Ave {
@Override
public void voar() { throw new UnsupportedOperationException("Pinguins não voam"); }
}
O problema aqui é que a classe Pinguim
quebra a expectativa da classe base. O correto seria criar uma hierarquia diferente:
Correto:
public class Ave {}
public class AveVoadora extends Ave {
public void voar() { /* Implementação de voo */ }
}
public class Pinguim extends Ave { /* Sem sobrescrever voar */ }
Isso garante que todas as sub-classes respeitem o comportamento esperado.
4. Interface Segregation Principle (ISP) - Princípio da Segregação de Interfaces
Nenhuma classe deve ser forçada a implementar métodos que não utiliza. Interfaces grandes e genéricas devem ser divididas em múltiplas interfaces menores e mais específicas.
Exemplo:
Errado:
public interface Funcionario {
void trabalhar();
void gerenciar();
}
public class Desenvolvedor implements Funcionario {
public void trabalhar() { /* Codando */ }
public void gerenciar() { throw new UnsupportedOperationException("Desenvolvedores não gerenciam"); }
}
Correto:
public interface Trabalhavel {
void trabalhar();
}
public interface Gerenciavel {
void gerenciar();
}
public class Desenvolvedor implements Trabalhavel {
public void trabalhar() { /* Codando */ }
}
public class Gerente implements Trabalhavel, Gerenciavel {
public void trabalhar() { /* Trabalha */ }
public void gerenciar() { /* Gerencia */ }
}
Isso evita a obrigação de implementar métodos desnecessários.
5. Dependency Inversion Principle (DIP) - Princípio da Inversão de Dependência
Módulos de alto nível não devem depender de módulos de baixo nível. Ambos devem depender de abstrações.
Exemplo:
Errado:
public class PedidoService {
private MySQLRepositorio repositorio;
public PedidoService() {
this.repositorio = new MySQLRepositorio();
}
}
Correto:
public interface Repositorio {
void salvar(Pedido pedido);
}
public class MySQLRepositorio implements Repositorio {
public void salvar(Pedido pedido) { /* Salva no MySQL */ }
}
public class PedidoService {
private Repositorio repositorio;
public PedidoService(Repositorio repositorio) {
this.repositorio = repositorio;
}
}
Agora podemos trocar o banco de dados sem alterar a lógica do PedidoService
.
Conclusão
Os princípios SOLID ajudam a criar um código mais organizado, modular e de fácil manutenção. Aplicá-los corretamente reduz o acoplamento e melhora a extensibilidade do sistema. Ao desenvolver software, tenha sempre em mente esses princípios para garantir qualidade e sustentabilidade do seu código.