SkillsTecnológicas
Menu
Back-end

Princípios SOLID: entenda cada princípio com exemplos

Entenda os cinco princípios SOLID com exemplos simples e aprenda a criar códigos mais coesos, flexíveis e fáceis de manter.

Marcos RodriguesPublicado em 5 de setembro de 2026Atualizado em 4 de setembro de 20268 min de leitura
Cinco módulos diferentes formam uma estrutura de software estável

Um sistema não se torna difícil de manter apenas porque cresceu. O problema aparece quando cada mudança exige alterar muitas classes, quando detalhes de infraestrutura contaminam regras de negócio ou quando uma abstração promete comportamentos que suas implementações não conseguem cumprir.

Os princípios SOLID ajudam a reconhecer e reduzir esses problemas. Eles orientam a divisão de responsabilidades, a criação de extensões e o controle de dependências para que o código possa evoluir com menos efeitos colaterais.

Neste guia, você entenderá cada letra de SOLID com exemplos simples, verá como os princípios se relacionam e aprenderá a aplicá-los sem transformar todo projeto em uma coleção desnecessária de interfaces e camadas.

O que são os princípios SOLID?

SOLID é um acrônimo para cinco princípios de design associados ao desenvolvimento orientado a objetos: Single Responsibility, Open-Closed, Liskov Substitution, Interface Segregation e Dependency Inversion. Robert C.

Martin reuniu esses conceitos em seu texto Design Principles and Design Patterns.

Os princípios não são regras de sintaxe nem padrões prontos. Eles oferecem critérios para avaliar responsabilidades e dependências.

Seu objetivo é reduzir sintomas como rigidez, fragilidade e dificuldade de reutilização, mas sempre exigem interpretação do contexto.

Eles são mais fáceis de visualizar em orientação a objetos, embora várias ideias — coesão, contratos, limites e dependências estáveis — também sejam úteis em outros paradigmas de programação.

S: Princípio da Responsabilidade Única

O Single Responsibility Principle afirma que um módulo deve ter uma única razão para mudar. “Responsabilidade” não significa obrigatoriamente um único método; significa atender a um ator, política ou eixo de mudança coeso.

Considere uma classe PedidoService que calcula preços, grava no banco, gera PDF e envia e-mail. Mudanças fiscais, de persistência, layout e comunicação atingem o mesmo componente. Separar cálculo, repositório, documento e notificação reduz essa mistura.

A separação deve seguir mudanças reais. Criar uma classe para cada linha produz fragmentação sem coesão. O sinal útil é perceber que diferentes áreas do negócio alteram o módulo por motivos independentes.

O: Princípio Aberto-Fechado

O Open-Closed Principle recomenda que entidades sejam abertas para extensão e fechadas para modificação.

A ideia não é proibir alterações, mas permitir novos comportamentos sem reescrever continuamente uma parte estável e já testada.

Um calculador de frete cheio de condicionais por transportadora cresce de forma arriscada.

Uma abstração CalculadoraFrete, com implementações separadas, permite incluir uma nova estratégia sem alterar as anteriores. Polimorfismo, composição e funções configuráveis podem viabilizar essa extensão.

Nem todo ponto precisa ser extensível antecipadamente. Abstrações devem surgir em torno de variações conhecidas ou muito prováveis; prever todas as mudanças cria complexidade especulativa.

Módulos especializados se conectam por interfaces pequenas e compatíveis

L: Princípio da Substituição de Liskov

O Liskov Substitution Principle exige que uma implementação possa substituir seu tipo base sem quebrar as expectativas válidas do cliente. O ponto central é comportamento, não apenas possuir os mesmos métodos.

Se uma interface Arquivo promete leitura e escrita, uma implementação somente leitura que lança exceção em salvar viola o contrato esperado.

A hierarquia compila, mas a substituição não é segura. Uma solução seria separar capacidades de leitura e escrita.

A formulação acadêmica vem do trabalho de Barbara Liskov sobre abstração e subtipos. Em uma apresentação do MIT sobre o poder da abstração, o princípio é resumido pela expectativa de que subtipos se comportem como seus supertipos quando usados por meio das operações do tipo base.

I: Princípio da Segregação de Interfaces

O Interface Segregation Principle diz que clientes não deveriam depender de operações que não utilizam. Interfaces amplas obrigam implementações a conhecer recursos irrelevantes e espalham o impacto de mudanças.

Uma interface Dispositivo com imprimir, digitalizar, grampear e enviar fax funciona para uma multifuncional, mas força uma impressora simples a implementar métodos sem sentido. Interfaces menores, orientadas às necessidades dos clientes, preservam contratos honestos.

Isso não significa criar uma interface por método. O objetivo é agrupar capacidades coesas e evitar dependências desnecessárias. O consumidor deve enxergar apenas o contrato de que realmente precisa.

D: Princípio da Inversão de Dependência

O Dependency Inversion Principle orienta módulos de alto nível e detalhes de baixo nível a dependerem de abstrações estáveis. Regras de negócio não deveriam conhecer diretamente detalhes como um cliente HTTP, framework ou banco específico.

Módulos substituíveis se conectam a um núcleo por contratos estáveis

Uma regra FinalizarPedido pode depender de um contrato GatewayPagamento. A implementação para um provedor externo depende desse contrato e pode ser trocada sem contaminar a política principal. O artigo DIP in the Wild mostra aplicações concretas dessa inversão.

A abstração deve pertencer à necessidade da política, não copiar toda a API do fornecedor. Caso contrário, a interface apenas muda o nome da dependência e mantém o mesmo acoplamento.

Como os cinco princípios se conectam

SRP cria módulos coesos. OCP procura pontos de extensão. LSP protege os contratos usados nessa extensão. ISP reduz contratos ao necessário. DIP direciona dependências para abstrações que representam políticas estáveis. Aplicados juntos, eles reduzem o alcance de uma mudança.

Mas um princípio não corrige automaticamente o outro. Muitas interfaces pequenas ainda podem representar responsabilidades mal divididas. Herança pode respeitar a forma e violar comportamento. O desenho precisa ser avaliado pelo fluxo do negócio e pelas mudanças que o sistema recebe.

Exemplo prático em um sistema de pedidos

Imagine uma API que fecha pedidos. A classe de caso de uso valida itens, calcula o total, cobra o cliente, salva o pedido e envia confirmação. Uma organização orientada por SOLID pode manter a coordenação no caso de uso e delegar cálculos, persistência, pagamento e notificação a colaboradores com contratos claros.

  • SRP: cada componente atende a um motivo de mudança.
  • OCP: novos meios de pagamento entram como novas implementações.
  • LSP: qualquer gateway respeita o mesmo contrato de resultado e erro.
  • ISP: o caso de uso recebe apenas as operações necessárias.
  • DIP: a regra depende de contratos, não do SDK do fornecedor.

Uma API RESTful pode aplicar esse desenho independentemente do framework. O ganho aparece quando integrações ou regras mudam e o impacto permanece localizado.

SOLID e injeção de dependência

Inversão de dependência é um princípio de arquitetura; injeção de dependência é uma técnica para fornecer colaboradores a um objeto. Um container de DI pode construir classes, mas não garante que as abstrações sejam boas. É possível injetar uma interface enorme e continuar fortemente acoplado.

Construtores explícitos costumam tornar dependências visíveis e facilitar testes. O conteúdo sobre injeção de dependência e escalabilidade complementa esse ponto com aplicações em sistemas web.

Quando aplicar SOLID

SOLID é especialmente valioso em domínios com regras relevantes, integrações substituíveis, equipes trabalhando em paralelo e software que evoluirá por bastante tempo. Use os princípios para orientar refatorações baseadas em dores observadas.

  • Uma classe muda por motivos independentes.
  • Condicionais crescem sempre que surge uma nova variação.
  • Implementações não conseguem cumprir o contrato do tipo base.
  • Clientes recebem métodos que nunca utilizam.
  • Regras de negócio importam detalhes de infraestrutura.

Testes ajudam a revelar acoplamento, mas testabilidade não deve ser o único objetivo. O desenho precisa continuar compreensível para quem mantém o sistema.

Quando SOLID vira exagero

Aplicar uma interface para cada classe, criar fábricas sem variação real e separar operações triviais em muitas camadas aumenta navegação e custo mental. Um protótipo ou automação curta pode não precisar do mesmo grau de flexibilidade de um sistema crítico.

O critério é o custo provável da mudança. Comece simples, identifique os eixos de variação e extraia abstrações quando elas representam conceitos estáveis. Como discute o artigo sobre por que a linguagem de programação não é o único fator, fundamentos e decisões de design têm mais peso que seguir fórmulas.

Erros comuns

  • Confundir responsabilidade única com “uma função por classe”.
  • Usar herança apenas para reaproveitar código, ignorando o contrato.
  • Criar interfaces que copiam fornecedores externos.
  • Preparar extensões que talvez nunca existam.
  • Aplicar padrões sem medir a clareza resultante.
  • Considerar código “SOLID” imune a mudanças de requisito.

Esses excessos podem gerar justamente alguns dos erros comuns na criação de sistemas: complexidade acidental, responsabilidades pouco claras e manutenção cara.

Perguntas frequentes

SOLID serve apenas para orientação a objetos?

Os princípios foram formulados nesse contexto, mas coesão, substituição, contratos pequenos e direção de dependências também ajudam outros estilos de programação.

SOLID é um design pattern?

Não. SOLID reúne princípios para avaliar design; padrões são soluções recorrentes para problemas específicos.

Preciso aplicar os princípios em alguma ordem?

Não existe uma ordem obrigatória. Comece pelo problema observado e use o princípio que melhor explica aquela pressão de mudança.

Toda classe precisa de uma interface?

Não. Uma abstração é útil quando representa um contrato relevante ou uma variação; criada mecanicamente, apenas adiciona indireção.

SOLID garante código limpo?

Não. Nomes, simplicidade, testes, tratamento de erros, domínio e colaboração da equipe continuam determinantes.

Como praticar SOLID?

Refatore exemplos pequenos, escreva testes de comportamento e compare o impacto de adicionar uma nova regra antes e depois da mudança.

Conclusão

Os princípios SOLID ajudam a manter responsabilidades coesas, extensões seguras, subtipos confiáveis, interfaces focadas e dependências orientadas a políticas.

O benefício real é reduzir o alcance e o risco das mudanças.

Eles funcionam melhor como perguntas de design, não como checklist automático. Se uma abstração torna o código mais difícil de compreender sem resolver uma variação concreta, simplifique.

Bom design não é o que contém mais camadas; é o que torna as decisões importantes claras e sustentáveis.