Princípios SOLID: Explicados de Forma Simples com Exemplos

Posted by

Se você programa em qualquer linguagem orientada a objetos — como Java, C#, Python ou TypeScript — provavelmente já ouviu falar dos Princípios SOLID. Eles são cinco diretrizes criadas para ajudar desenvolvedores a escreverem código mais limpo, flexível, fácil de manter e de testar.

SOLID é um acrônimo formado pelas iniciais de cinco princípios:

  • S — Single Responsibility Principle (Princípio da Responsabilidade Única)
  • O — Open/Closed Principle (Princípio Aberto/Fechado)
  • L — Liskov Substitution Principle (Princípio da Substituição de Liskov)
  • I — Interface Segregation Principle (Princípio da Segregação de Interface)
  • D — Dependency Inversion Principle (Princípio da Inversão de Dependência)

Vamos explicar cada um deles de forma simples, com exemplos práticos em pseudocódigo (fácil de entender, independente da linguagem).

1. S — Single Responsibility Principle (Responsabilidade Única)

“Uma classe deve ter apenas um motivo para mudar.”

Isso significa que cada classe (ou função) deve fazer uma única coisa bem feita. Se ela tem mais de uma responsabilidade, fica difícil de manter, testar e reutilizar.

Exemplo Ruim:

class Relatorio:
    def gerar_relatorio(self, dados):
        return f"Relatório: {dados}"

    def salvar_em_arquivo(self, conteudo, nome_arquivo):
        with open(nome_arquivo, 'w') as f:
            f.write(conteudo)

    def enviar_por_email(self, conteudo, email):
        print(f"Enviando '{conteudo}' para {email}")

Essa classe faz três coisas: gerar, salvar e enviar. Se a forma de salvar mudar, ou o formato do e-mail, você precisa alterar a mesma classe — e isso viola o SRP.

Exemplo Corrigido:

class GeradorRelatorio:
    def gerar(self, dados):
        return f"Relatório: {dados}"

class SalvadorArquivo:
    def salvar(self, conteudo, nome_arquivo):
        with open(nome_arquivo, 'w') as f:
            f.write(conteudo)

class EnviadorEmail:
    def enviar(self, conteudo, email):
        print(f"Enviando '{conteudo}' para {email}")

Agora cada classe tem uma única responsabilidade. Fácil de testar, modificar e reutilizar.

2. O — Open/Closed Principle (Aberto para Extensão, Fechado para Modificação)

“Entidades de software (classes, módulos, funções) devem estar abertas para extensão, mas fechadas para modificação.”

Ou seja: você deve poder adicionar novos comportamentos sem alterar o código existente.

Exemplo Ruim:

class CalculadoraDesconto:
    def calcular(self, tipo_cliente, valor):
        if tipo_cliente == "regular":
            return valor * 0.9
        elif tipo_cliente == "premium":
            return valor * 0.8
        elif tipo_cliente == "vip":
            return valor * 0.7

Se amanhã surgir um tipo “parceiro”, você precisa mexer nessa classe — o que pode quebrar algo.

Exemplo Corrigido:

from abc import ABC, abstractmethod

class CalculadorDesconto(ABC):
    @abstractmethod
    def calcular(self, valor):
        pass

class DescontoRegular(CalculadorDesconto):
    def calcular(self, valor):
        return valor * 0.9

class DescontoPremium(CalculadorDesconto):
    def calcular(self, valor):
        return valor * 0.8

class CalculadoraDesconto:
    def __init__(self, estrategia: CalculadorDesconto):
        self.estrategia = estrategia

    def calcular(self, valor):
        return self.estrategia.calcular(valor)

Agora, para adicionar um novo tipo de desconto, basta criar uma nova classe — sem alterar a classe principal.

3. L — Liskov Substitution Principle (Substituição de Liskov)

“Objetos de uma superclasse devem poder ser substituídos por objetos de suas subclasses sem quebrar o comportamento do programa.”

Em outras palavras: se Pato herda de Ave, então qualquer código que use Ave deve funcionar corretamente com Pato.

Exemplo Ruim:

class Ave:
    def voar(self):
        pass

class Pato(Ave):
    def voar(self):
        print("Pato voando!")

class Avestruz(Ave):
    def voar(self):
        raise Exception("Avestruz não voa!")

Se você tem um código que espera que toda Ave voe, e passa um Avestruz, vai dar erro. Isso viola Liskov.

Exemplo Corrigido:

class Ave:
    pass

class AveQueVoa(Ave):
    def voar(self):
        pass

class Pato(AveQueVoa):
    def voar(self):
        print("Pato voando!")

class Avestruz(Ave):
    pass

Agora, só as aves que realmente voam herdam de AveQueVoa. Ninguém vai tentar fazer uma avestruz voar — problema resolvido.

4. I — Interface Segregation Principle (Segregação de Interface)

“Clientes não devem ser forçados a depender de interfaces que não usam.”

Em vez de criar interfaces “gigantes”, crie interfaces menores e específicas. Assim, as classes só implementam o que realmente precisam.

Exemplo Ruim:

class MaquinaDeCafe:
    def fazer_cafe(self):
        pass
    def fazer_cha(self):
        pass
    def aquecer_agua(self):
        pass

class CafeteiraSimples(MaquinaDeCafe):
    def fazer_cafe(self):
        print("Fazendo café...")

    def fazer_cha(self):
        raise NotImplementedError("Essa cafeteira não faz chá!")

    def aquecer_agua(self):
        print("Água aquecida.")

A CafeteiraSimples é forçada a implementar métodos que não usa — isso é ruim.

Exemplo Corrigido:

class Aquecedor:
    def aquecer_agua(self):
        pass

class FazCafe:
    def fazer_cafe(self):
        pass

class FazCha:
    def fazer_cha(self):
        pass

class CafeteiraSimples(Aquecedor, FazCafe):
    def aquecer_agua(self):
        print("Água aquecida.")

    def fazer_cafe(self):
        print("Fazendo café...")

Agora a cafeteira só implementa o que precisa. Se um dia surgir uma máquina que faça só chá, ela pode implementar FazCha e Aquecedor, sem problemas.

5. D — Dependency Inversion Principle (Inversão de Dependência)

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

Módulos de alto nível (ex: lógica de negócio) não devem depender de módulos de baixo nível (ex: acesso ao banco). Ambos devem depender de abstrações (interfaces).

Exemplo Ruim:

class BancoDeDadosMySQL:
    def salvar(self, usuario):
        print(f"Salvando {usuario} no MySQL")

class ServicoUsuario:
    def __init__(self):
        self.db = BancoDeDadosMySQL()

    def criar_usuario(self, nome):
        usuario = {"nome": nome}
        self.db.salvar(usuario)

Se amanhã você quiser trocar MySQL por PostgreSQL, terá que mexer no ServicoUsuario — o que não é ideal.

Exemplo Corrigido:

from abc import ABC, abstractmethod

class RepositorioUsuario(ABC):
    @abstractmethod
    def salvar(self, usuario):
        pass

class BancoDeDadosMySQL(RepositorioUsuario):
    def salvar(self, usuario):
        print(f"Salvando {usuario} no MySQL")

class BancoDeDadosPostgreSQL(RepositorioUsuario):
    def salvar(self, usuario):
        print(f"Salvando {usuario} no PostgreSQL")

class ServicoUsuario:
    def __init__(self, repositorio: RepositorioUsuario):
        self.repositorio = repositorio

    def criar_usuario(self, nome):
        usuario = {"nome": nome}
        self.repositorio.salvar(usuario)

Agora, para trocar o banco, basta passar outra implementação no construtor — sem alterar o serviço.

Conclusão

Os princípios SOLID não são regras rígidas, mas diretrizes valiosas para escrever código mais sustentável. Eles ajudam a:

  • Evitar código difícil de entender e modificar
  • Facilitar testes
  • Permitir evolução do sistema sem quebrar tudo
  • Melhorar a colaboração em equipe

Você não precisa aplicar tudo perfeitamente desde o início — comece aos poucos. Refatore um pouco por vez. Com o tempo, seu código vai ficar mais robusto, legível e profissional.

“SOLID não é sobre perfeição. É sobre evolução contínua do design do seu código.”

Dica final: sempre que for criar uma nova classe ou modificar uma existente, pergunte-se:

  • Ela tem só uma responsabilidade? → S
  • Posso estender sem modificar? → O
  • Posso substituir por uma subclasse sem problemas? → L
  • Estou implementando só o que preciso? → I
  • Estou dependendo de abstrações, não de detalhes? → D

Se sim, você está no caminho certo.

Leave a Reply

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *