Na Parte 2, vamos transformar esses conceitos em código real.
Para entender melhor os conceitos, recomendo ler antes a Parte 1, caso ainda não tenha visto.

A ideia é ir além da teoria e observar, diretamente no código:

  • exemplos antes e depois (violação -> refatoração),
  • diagramas de dependência aplicados ao código,
  • como o SOLID melhora testabilidade, isolamento e clareza,
  • e um exercício prático completo:
    uma Central de Notificações com Email, SMS e Push, aplicando SRP, OCP e DIP de ponta a ponta.

A parte prática será demonstrada em Python, mas os princípios se aplicam a qualquer linguagem.
A resolução final do exercício foi feita em Java, reforçando como o SOLID é universal, independentemente da sintaxe.
O objetivo aqui é fechar o ciclo: entender como cada princípio se manifesta em decisões reais de design, não apenas no texto, mas em código que você pode testar, evoluir e refatorar.

Pontos de atenção:
No Python, não existe a palavra-chave interface como em Java.
Por isso, usamos o módulo abc (Abstract Base Class) e o decorator @abstractmethod para definir abstrações, contratos e polimorfismo seguro, permitindo aplicar OCP, LSP, ISP e DIP com precisão.

💡 Dica:

Quando você sentir que uma mudança simples exige mexer em vários lugares, é um sinal de que um dos princípios SOLID foi violado.
O segredo não é decorar, mas sentir o incômodo e saber onde ajustar.

S — Single Responsibility Principle (Responsabilidade Única)

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

Antes (violando o SRP)

class SistemaPedido:
    def realizar_pedido(self, produto: str, quantidade: int):
        # validação
        if not isinstance(produto, str) or quantidade <= 0:
            print("Erro: dados inválidos.")
            return
        
        # cálculo do valor
        total = quantidade * 10  
        print(f"Pedido confirmado: {quantidade}x {produto} — total R${total}")

Problema: Podemos observar que a classe SistemaPedido realiza a validação, calcula o preço e gera confirmação, possui um total de três responsabilidades.

Depois (respeitando SRP)

class PedidoValidador:
    def validar(self, produto: str, quantidade: int) -> bool:
        return isinstance(produto, str) and quantidade > 0


class PedidoPreco:
    def calcular(self, quantidade: int) -> float:
        return quantidade * 10  # mock de cálculo real


class PedidoConfirmacao:
    def enviar(self, produto: str, quantidade: int, total: float):
        print(f"Pedido confirmado: {quantidade}x {produto} — total R${total}")


class SistemaPedido:
    def __init__(self, validador: PedidoValidador, preco: PedidoPreco, confirmacao: PedidoConfirmacao):
        self.validador = validador
        self.preco = preco
        self.confirmacao = confirmacao

    def realizar_pedido(self, produto, quantidade):
        if not self.validador.validar(produto, quantidade):
            print("Erro: dados inválidos.")
            return
        
        total = self.preco.calcular(quantidade)
        self.confirmacao.enviar(produto, quantidade, total)
AntesDepois
Classe fazia tudoCada uma tem papel definido
Mudanças quebram tudoMudanças isoladas
Testes difíceisTestes simples e focados

Resumo:

SRP é sobre “motivos de mudança”.
Se uma classe muda por mais de um motivo, ela tem responsabilidades demais.
Separe em componentes pequenos, coesos e previsíveis.

O — Open/Closed Principle (Aberto/Fechado)

“Entidades de software devem estar abertas para extensão, mas fechadas para modificação.”

Antes (violando o OCP)

class EntregaService:
    def entregar(self, metodo):
        if metodo == "moto":
            print("Entrega via moto")
        elif metodo == "correios":
            print("Entrega via Correios")
        elif metodo == "drone":
            print("Entrega via drone")

Problema: toda vez que entra um novo meio de entrega, exige modificar a classe EntregaService.
Isso quebra o OCP e gera um alto acoplamento crescente com o tempo.

Depois (respeitando o OCP)

from abc import ABC, abstractmethod

class Entrega(ABC):
    @abstractmethod
    def enviar(self):
        pass


class EntregaMoto(Entrega):
    def enviar(self):
        print("Entrega realizada por moto")


class EntregaCorreios(Entrega):
    def enviar(self):
        print("Entrega enviada pelos Correios")


class EntregaDrone(Entrega):
    def enviar(self):
        print("Entrega enviada por drone")


class EntregaService:
    def processar(self, metodo: Entrega):
        print("Processando entrega...")
        metodo.enviar()
        print("Entrega concluída.")
AntesDepois
Muitos if/elsePolimorfismo via abstração
Novo tipo exige alterar código existenteBasta criar nova classe
Acoplamento altoBaixo acoplamento e extensibilidade
Classe central conhece todos os detalhesClasse central depende só do contrato

Resumo: A classe EntregaService, não conhece mais os tipos concretos. Apenas trabalha com abstração Entrega.
OCP não impede mudanças, ele controla onde elas ocorrem.
Você adiciona comportamento criando novas classes, não alterando as antigas.

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

“Subtipos devem ser substituíveis por seus tipos base sem alterar o comportamento do programa.”

Antes (violando o LSP)

class Transporte:
    def carregar(self): pass
    def descarregar(self): pass
    def refrigerar(self): pass


class CaminhaoFrigorifico(Transporte):
    def carregar(self):
        print("Caminhão carregando...")
    def descarregar(self):
        print("Caminhão descarregando...")
    def refrigerar(self):
        print("Ativando refrigeração...")


class Moto(Transporte):
    def carregar(self):
        print("Moto carregando...")
    def descarregar(self):
        print("Moto descarregando...")
    def refrigerar(self):
        raise NotImplementedError("Moto não possui refrigeração!")

Problema: A classe Moto herda um método que não faz sentido ser utilizado, pois não possui refrigeração.
Se um código espera um Transporte completo (com refrigerar()), a moto quebra o programa, violando diretamente o LSP.

Depois (respeitando o LSP + ISP)

from abc import ABC, abstractmethod

class Transporte(ABC):
    @abstractmethod
    def carregar(self): pass

    @abstractmethod
    def descarregar(self): pass


class Refrigerado(ABC):
    @abstractmethod
    def refrigerar(self): pass


class CaminhaoFrigorifico(Transporte, Refrigerado):
    def carregar(self):
        print("Caminhão frigorífico carregando...")

    def descarregar(self):
        print("Caminhão frigorífico descarregando...")

    def refrigerar(self):
        print("Refrigeração ativada")


class Moto(Transporte):
    def carregar(self):
        print("Moto carregando...")

    def descarregar(self):
        print("Moto descarregando...")


def iniciar(transporte: Transporte):
    transporte.carregar()
    transporte.descarregar()

iniciar(Moto())
iniciar(CaminhaoFrigorifico())
AntesDepois
Herança forçadaContratos específicos e coesos
Exceção inesperadaComportamento previsível
Polimorfismo quebradoSubstituição segura
Superclasse infladaInterfaces focadas (LSP + ISP)

Resumo: LSP é sobre confiabilidade do polimorfismo.
Se a subclasse muda as regras do tipo base, o contrato foi quebrado.

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

“Nenhum cliente deve depender de métodos que não usa.”

Antes (violando o ISP)

class Transporte:
    def carregar(self): pass
    def descarregar(self): pass
    def refrigerar(self): pass
    def entrega_expressa(self): pass  

class Caminhao(Transporte):
    def carregar(self):
        print("Caminhão carregando...")
    def descarregar(self):
        print("Caminhão descarregando...")
    def refrigerar(self):
        print("Ativando refrigeração...")
	def entrega_expressa(self):
        raise NotImplementedError("Caminhão não faz entrega expressa!")

class Moto(Transporte):
    def carregar(self):
        print("Moto carregando...")
    def descarregar(self):
        print("Moto descarregando...")
    def refrigerar(self):
        raise NotImplementedError("Moto não possui refrigeração!")
    def entrega_expressa(self):
        raise NotImplementedError("Moto não faz entrega expressa!")

Problema: Interface Transporte obriga todos os transportes a implementarem métodos que não fazem parte de sua responsabilidade.
Surgem implementações artificiais, exceções e contratos falsos.
Ou seja, exatamente o que o ISP evita.

Depois (respeitando o ISP)

from abc import ABC, abstractmethod

class Transporte(ABC):
    @abstractmethod
    def entregar(self): pass


class Refrigerado(ABC):
    @abstractmethod
    def refrigerar(self): pass


class Express(ABC):
    @abstractmethod
    def entrega_expressa(self): pass


class Moto(Transporte):
    def entregar(self):
        print("Moto entregando...")


class CaminhaoFrigorifico(Transporte, Refrigerado):
    def entregar(self):
        print("Caminhão frigorífico entregando...")

    def refrigerar(self):
        print("Refrigeração ativada.")


class MotoExpress(Transporte, Express):
    def entregar(self):
        print("Moto expressa entregando...")

    def entrega_expressa(self):
        print("Entrega expressa realizada!")

Moto().entregar()
MotoExpress().entrega_expressa()
CaminhaoFrigorifico().refrigerar()
AntesDepois
Interface genérica demaisInterfaces pequenas e focadas
Métodos sem sentidoContratos coesos
Classes dependem do que não usamCada classe implementa o que precisa
Exceções desnecessáriasComportamento válido

Resumo: ISP é SRP aplicado às interfaces: um motivo para mudar = um cliente que a usa.

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

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

Antes (violando o DIP)

class PagamentoCartao:
    def pagar(self, valor):
        print(f"Pagando {valor} com cartão...")


class Checkout:
    def __init__(self):
        self.pagamento = PagamentoCartao()  # acoplado

    def finalizar(self, valor):
        self.pagamento.pagar(valor)

Problema: O Checkout depende diretamente de um detalhe concreto: PagamentoCartao.
Se surgir Pix, Boleto, PayPal, criptomoeda, QRCode, transferência bancária etc., será necessário alterar a classe Checkout.

Depois (respeitando o DIP)

from abc import ABC, abstractmethod

class Pagamento(ABC):
    @abstractmethod
    def pagar(self, valor): pass


class PagamentoCartao(Pagamento):
    def pagar(self, valor):
        print(f"Pagando {valor} com cartão...")


class PagamentoPix(Pagamento):
    def pagar(self, valor):
        print(f"Pagando {valor} com Pix...")


class Checkout:
    def __init__(self, metodo: Pagamento):
        self.metodo = metodo  # depende da abstração

    def finalizar(self, valor):
        self.metodo.pagar(valor)
        
checkout = Checkout(PagamentoPix())
checkout.finalizar(150)
AntesDepois
Alto nível depende do baixo nívelAmbos dependem de abstrações
Implementação fixa e rígidaImplementação plugável
Testes difíceisFácil simular dependências

Resumo: O DIP define a direção da dependência.
O alto nível não conhece detalhes de pagamento, transporte, banco, serviço externo, driver, etc.

Resumo geral:

PrincípioO que muda no códigoBenefício
SRPUma responsabilidade por classeClareza e isolamento
OCPExtensão via abstraçãoCódigo estável e flexível
LSPSubclasses previsíveisPolimorfismo confiável
ISPInterfaces pequenasBaixo acoplamento
DIPAlto nível depende de abstraçõesArquitetura limpa

Exercício final: Central de Notificações

Crie um sistema que envie mensagens por Email, SMS e Push, aplicando SOLID:

  1. Interface Notificacao define o contrato.
  2. EmailNotificacao, SMSNotificacao e PushNotificacao implementam o contrato.
  3. CentralDeNotificacoes injeta a abstração e envia mensagens.
  4. Novos tipos podem ser adicionados sem modificar a central (OCP).
  5. Cada classe tem uma responsabilidade clara (SRP).
  6. A central não conhece detalhes concretos (DIP).

O gabarito do exercício está escrito em Java, disponível no meu Github: https://github.com/biaggiorizzo/biaggio-blog-code/tree/main/concepts/poo/solid

Conclusão

Esses exemplos mostram como SOLID não é só teoria, mas é a base de arquiteturas sustentáveis, onde mudanças são simples e seguras.

Referências