← Todos os artigos

SOLID na prática: além das definições de manual

Os cinco princípios SOLID explicados com exemplos reais em Java — quando aplicar, quando relaxar e o que cada um realmente protege.

Quase todo mundo decora a sigla SOLID para entrevistas, mas poucos param para pensar no que cada letra está realmente protegendo. SOLID não é sobre seguir regras — é sobre controlar o custo da mudança. Vamos passar pelos cinco princípios com código Java, focando na intuição por trás de cada um.

S — Single Responsibility Principle

Uma classe deve ter um, e apenas um, motivo para mudar. O erro clássico é misturar regra de negócio com detalhe de infraestrutura:

// ❌ Faz cálculo E persistência E formatação de e-mail
public class Invoice {
    public BigDecimal total() { /* regra de negócio */ }
    public void saveToDatabase() { /* detalhe de infra */ }
    public String toEmailBody() { /* detalhe de apresentação */ }
}

Cada responsabilidade muda por um motivo diferente — e por uma pessoa diferente do time. Separe-as:

public class Invoice {
    public BigDecimal total() { /* só regra de negócio */ }
}

public class InvoiceRepository { /* persistência */ }
public class InvoiceEmailFormatter { /* apresentação */ }

O — Open/Closed Principle

Entidades devem estar abertas para extensão, fechadas para modificação. Na prática: você adiciona comportamento novo sem editar o código existente.

public interface DiscountPolicy {
    BigDecimal apply(BigDecimal amount);
}

// Um novo tipo de desconto é uma classe nova — não um if a mais.
public class BlackFridayDiscount implements DiscountPolicy {
    public BigDecimal apply(BigDecimal amount) {
        return amount.multiply(new BigDecimal("0.70"));
    }
}

L — Liskov Substitution Principle

Um subtipo precisa ser usável em qualquer lugar onde o tipo base é esperado, sem surpresas. Se uma subclasse lança exceção onde a base funcionava, você quebrou Liskov. O exemplo canônico é o Square extends Rectangle — mexer na largura do quadrado muda a altura, violando a expectativa de quem usa Rectangle.

I — Interface Segregation Principle

Melhor várias interfaces específicas do que uma genérica e gorda. Ninguém deve ser forçado a implementar métodos que não usa:

// ❌ Quem só lê é obrigado a implementar write()
interface Repository<T> {
    T findById(long id);
    void save(T entity);
    void delete(long id);
}

// ✅ Segregado por capacidade
interface ReadRepository<T> { T findById(long id); }
interface WriteRepository<T> { void save(T entity); }

D — Dependency Inversion Principle

Módulos de alto nível não devem depender de detalhes — ambos dependem de abstrações. É o que torna seu núcleo de negócio testável e independente do banco, do broker ou do framework.

public class CheckoutService {
    private final PaymentGateway gateway; // interface, não Stripe direto

    public CheckoutService(PaymentGateway gateway) {
        this.gateway = gateway;
    }
}

O ponto que fica

SOLID é um conjunto de heurísticas, não dogma. O objetivo final é sempre o mesmo: deixar o código barato de mudar quando o mundo muda — e o mundo sempre muda. Aplique com julgamento, não no piloto automático.