🧱 Princípios SOLID - Fundamentos da Orientação a Objetos

Crie sistemas mais coesos, flexíveis e fáceis de manter.


📘 Introdução

Os princípios SOLID são um conjunto de boas práticas de programação orientada a objetos, formulados por Robert C. Martin (Uncle Bob).
O objetivo é melhorar a legibilidade, extensibilidade e manutenibilidade do código, reduzindo o acoplamento entre classes.

O acrônimo SOLID representa cinco princípios:

LetraNomeTradução
SSingle Responsibility PrinciplePrincípio da Responsabilidade Única
OOpen/Closed PrinciplePrincípio do Aberto/Fechado
LLiskov Substitution PrinciplePrincípio da Substituição de Liskov
IInterface Segregation PrinciplePrincípio da Segregação de Interfaces
DDependency Inversion PrinciplePrincípio da Inversão de Dependência

🧩 1. Single Responsibility Principle (SRP)

Uma classe deve ter apenas um motivo para mudar.

Cada classe deve ter uma única responsabilidade clara, evitando que uma mudança em uma funcionalidade impacte outras partes do sistema.

❌ Exemplo incorreto

public class RelatorioService {     
	public void gerarRelatorio() {         
		// Lógica para gerar relatório     
	}      
	public void enviarEmail() {         
		// Lógica para enviar relatório por e-mail     
	} 
}

➡️ Aqui, a classe faz duas coisas: gerar e enviar relatórios.

✅ Exemplo correto

public class GeradorRelatorio {     
	public void gerar() { 
		/* ... */ 
	} 
}  
 
public class EnviadorEmail {     
	public void enviar() { 
		/* ... */ 
	} 
}

➡️ Agora cada classe tem uma única responsabilidade e pode evoluir de forma independente.


🧱 2. Open/Closed Principle (OCP)

As classes devem estar abertas para extensão, mas fechadas para modificação.

Isso significa que devemos evitar alterar código existente, e sim estendê-lo para incluir novos comportamentos.

❌ Exemplo incorreto

public class CalculadoraDeDesconto {
    public double calcular(String tipoCliente) {
        if (tipoCliente.equals("VIP")) return 0.2;
        else if (tipoCliente.equals("COMUM")) return 0.1;
        return 0.0;
    }
}

➡️ Cada vez que surgir um novo tipo de cliente, será necessário modificar a classe.

✅ Exemplo correto (com polimorfismo)

public interface Desconto {
    double calcular();
}
 
public class DescontoVip implements Desconto {
    public double calcular() { return 0.2; }
}
 
public class DescontoComum implements Desconto {
    public double calcular() { return 0.1; }
}

➡️ Agora podemos adicionar novas regras sem alterar o código existente — basta criar novas classes.


🧠 3. Liskov Substitution Principle (LSP)

Subtipos devem poder substituir seus tipos base sem alterar o comportamento esperado.

Isso significa que uma subclasse deve poder ser usada no lugar da classe pai, sem causar erros ou resultados inesperados.

❌ Exemplo incorreto

 public class Ave {
    public void voar() { System.out.println("Voando..."); }
}
 
public class Pinguim extends Ave {
    @Override
    public void voar() {
        throw new UnsupportedOperationException("Pinguins não voam!");
    }
}

➡️ Aqui, Pinguim viola o LSP, pois não respeita o comportamento da classe base.

✅ Exemplo correto

public abstract class Ave { }
 
public class AveQueVoa extends Ave {
    public void voar() { System.out.println("Voando..."); }
}
 
public class Pinguim extends Ave { }

➡️ Agora o comportamento é coerente com a hierarquia, e cada tipo representa corretamente sua natureza.


⚙️ 4. Interface Segregation Principle (ISP)

Nenhuma classe deve ser forçada a implementar métodos que não usa.

Evite interfaces muito grandes ou genéricas — prefira interfaces específicas para cada contexto.

❌ Exemplo incorreto

 public interface Funcionario {
    void programar();
    void testar();
    void gerenciar();
}

➡️ Um desenvolvedor precisará implementar métodos de gestão, e um gerente terá métodos que não usa.

✅ Exemplo correto

public interface Programador {
    void programar();
}
 
public interface Testador {
    void testar();
}
 
public interface Gerente {
    void gerenciar();
}

➡️ Cada interface representa uma função específica, promovendo baixo acoplamento.


🧭 5. Dependency Inversion Principle (DIP)

Dependa de abstrações, não de implementações.

As classes de alto nível não devem depender diretamente de classes de baixo nível — ambas devem depender de interfaces ou abstrações.

❌ Exemplo incorreto

public class MySQLRepository {
    public void salvar() { /* ... */ }
}
 
public class UsuarioService {
    private MySQLRepository repo = new MySQLRepository();
}

➡️ O UsuarioService depende diretamente de uma implementação específica (MySQLRepository).

✅ Exemplo correto

public interface UsuarioRepository {
    void salvar();
}
 
public class MySQLRepository implements UsuarioRepository {
    public void salvar() { /* ... */ }
}
 
public class UsuarioService {
    private final UsuarioRepository repo;
 
    public UsuarioService(UsuarioRepository repo) {
        this.repo = repo;
    }
}
 

➡️ Agora o UsuarioService depende de uma abstração, permitindo trocar implementações (ex: MongoDB, Postgres, API externa) sem alterar o código.


🧩 Benefícios dos Princípios SOLID

✅ Código mais modular e reutilizável
Facilidade de manutenção e evolução
✅ Maior testabilidade (isolamento e mocks simples)
✅ Redução de efeitos colaterais e regressões
✅ Arquitetura mais flexível e sustentável a longo prazo


🧠 Mapa Mental — Princípios SOLID

mindmap
  root((SOLID))
    S((S - Single Responsibility))
      ::icon(fa-solid fa-user-gear)
      "Uma classe deve ter apenas uma responsabilidade"
      "Facilita manutenção e evita efeitos colaterais"
      Exemplo:::code["Gerar relatório separado de enviar e-mail"]
    O((O - Open/Closed))
      ::icon(fa-solid fa-lock-open)
      "Aberto para extensão, fechado para modificação"
      "Use herança ou interfaces para adicionar comportamentos"
      Exemplo:::code["Nova regra de desconto sem alterar a classe base"]
    L((L - Liskov Substitution))
      ::icon(fa-solid fa-diagram-project)
      "Subclasses devem substituir a classe base sem alterar o comportamento esperado"
      Exemplo:::code["Evitar que 'Pinguim' herde de 'AveQueVoa'"]
    I((I - Interface Segregation))
      ::icon(fa-solid fa-sitemap)
      "Divida interfaces grandes em menores e específicas"
      Exemplo:::code["Programador, Testador e Gerente em interfaces separadas"]
    D((D - Dependency Inversion))
      ::icon(fa-solid fa-link)
      "Dependa de abstrações, não de implementações concretas"
      Exemplo:::code["Service depende de interface, não do repositório MySQL"]
 

📚 Resumo Visual

flowchart LR
    S[Single Responsibility] --> O[Open/Closed]
    O --> L[Liskov Substitution]
    L --> I[Interface Segregation]
    I --> D[Dependency Inversion]
    classDef solid fill:#0d9488,stroke:#fff,color:#fff;
    class S,O,L,I,D solid;

🔗 Referências