Princípios SOLID em Java: da teoria à prática
SOLID é um acrônimo de cinco princípios fundamentais da programação orientada a objetos, formulados por Robert Martin (Uncle Bob). Esses princípios ajudam os desenvolvedores a criar código flexível, sustentável e facilmente extensível. Neste artigo, vamos analisar cada princípio SOLID com exemplos em Java, desde a teoria simples até a prática real.
1. Single Responsibility Principle (SRP) — Princípio da Responsabilidade Única
Teoria: Uma classe deve ter apenas um motivo para mudar. Em outras palavras, cada classe deve ser responsável por exatamente uma funcionalidade.
Violação do SRP:
public class Employee { public void calculatePay() { /* cálculo do salário */ } public void saveToDatabase() { /* salvar no BD */ } public void generateReport() { /* gerar relatório */ }}Correção:
public class Employee { /* apenas dados */ }
public class PayCalculator { public void calculatePay(Employee emp) { /* ... */ }}
public class EmployeeRepository { public void save(Employee emp) { /* ... */ }}
public class ReportGenerator { public void generate(Employee emp) { /* ... */ }}Agora cada classe tem uma responsabilidade clara, o que simplifica os testes e a manutenção.
2. Open-Closed Principle (OCP) — Princípio Aberto/Fechado
Teoria: Entidades de software (classes, módulos, funções) devem estar abertas para extensão, mas fechadas para modificação.
Violação do OCP:
public class ShapeDrawer { public void draw(String shapeType) { if ("circle".equals(shapeType)) { // desenhar círculo } else if ("rectangle".equals(shapeType)) { // desenhar retângulo } }}Correção usando polimorfismo:
public interface Shape { void draw();}
public class Circle implements Shape { @Override public void draw() { /* desenhar círculo */ }}
public class Rectangle implements Shape { @Override public void draw() { /* desenhar retângulo */ }}
public class ShapeDrawer { public void draw(Shape shape) { shape.draw(); }}Agora, para adicionar uma nova forma (por exemplo, Triangle), não é necessário alterar o código existente — basta criar uma nova classe que implemente a interface Shape.
3. Liskov Substitution Principle (LSP) — Princípio da Substituição de Liskov
Teoria: Os objetos em um programa devem ser substituíveis por instâncias de seus subtipos sem alterar a correção do programa. Se S é um subtipo de T, então objetos do tipo T podem ser substituídos por objetos do tipo S sem quebrar o funcionamento.
Violação clássica do LSP:
public class Rectangle { protected int width, height; public void setWidth(int w) { width = w; } public void setHeight(int h) { height = h; } public int getArea() { return width * height; }}
public class Square extends Rectangle { @Override public void setWidth(int w) { super.setWidth(w); super.setHeight(w); } @Override public void setHeight(int h) { super.setWidth(h); super.setHeight(h); }}Um cliente que trabalha com Rectangle espera que alterar a largura não afete a altura. O quadrado viola essa expectativa. Solução: Usar uma interface comum ou classes separadas sem herança.
4. Interface Segregation Principle (ISP) — Princípio da Segregação de Interfaces
Teoria: Os clientes não devem depender de métodos que não usam. É melhor ter várias interfaces especializadas do que uma interface "gorda".
Violação do ISP: