Princípios SOLID em Java: da teoria à prática avançada
Os princípios SOLID são a base do design orientado a objetos, que ajuda desenvolvedores a criar código resistente a mudanças, facilmente testável e escalável. Para um desenvolvedor Java, entender esses princípios em nível avançado não é apenas conhecer as definições, mas saber identificar suas violações em código legado e aplicar padrões para refatoração. Neste artigo, analisaremos cada princípio com foco em situações complexas, porém típicas.
1. Single Responsibility Principle (SRP): A linha entre responsabilidade e coesão
Teoria: Uma classe deve ter apenas um motivo para mudar. Na prática, isso significa que a classe deve ser responsável por uma parte da funcionalidade do programa.
Prática avançada: Frequentemente, a violação do SRP não se manifesta em uma mistura explícita de lógicas, mas em uma responsabilidade "difusa". Considere o exemplo de uma classe para trabalhar com relatórios:
// Violação do SRP: a classe lida com dados, formatação e enviopublic class ReportService { public void generateReport(List<Data> data) { // Lógica de cálculos Map<String, Integer> aggregated = aggregate(data); // Formatação (HTML) String html = formatToHtml(aggregated); // Envio por email sendEmail(html, "admin@example.com"); }
private Map<String, Integer> aggregate(List<Data> data) { // ... return new HashMap<>(); }
private String formatToHtml(Map<String, Integer> data) { // ... return "<html>...</html>"; }
private void sendEmail(String content, String to) { // ... }}Problema: se o formato do relatório mudar (precisar de PDF em vez de HTML), essa classe terá que ser alterada. Se o método de envio mudar (Telegram em vez de email) — novamente. Solução — dividir em três classes:
public class ReportAggregator { public Map<String, Integer> aggregate(List<Data> data) { ... }}
public class HtmlReportFormatter { public String format(Map<String, Integer> data) { ... }}
public class EmailSender { public void send(String content, String to) { ... }}
// Composição no serviçopublic class ReportGenerationFacade { private ReportAggregator aggregator; private HtmlReportFormatter formatter; private EmailSender sender;
public void generateAndSend(List<Data> data) { var aggregated = aggregator.aggregate(data); var html = formatter.format(aggregated); sender.send(html, "admin@example.com"); }}2. Open-Closed Principle (OCP): A arte da extensão através de abstrações
Teoria: As classes devem estar abertas para extensão, mas fechadas para modificação.
Prática avançada: Uma violação típica são cadeias de if-else ou switch para diferentes tipos de objetos. A maneira avançada é usar o padrão Strategy ou Template Method com polimorfismo.
// Violação do OCP: ao adicionar um novo tipo de pagamento, é preciso alterar a classepublic class PaymentProcessor { public void processPayment(String type, double amount) { if ("CREDIT_CARD".equals(type)) { // lógica para cartão de crédito } else if ("PAYPAL".equals(type)) { // lógica para PayPal } else if ("BITCOIN".equals(type)) { // lógica para Bitcoin } }}Correção através de interface e fábrica:
public interface PaymentMethod { void pay(double amount);}
public class CreditCardPayment implements PaymentMethod { public void pay(double amount) { ... }}
public class PayPalPayment implements PaymentMethod