Módulo 10: Paradigmas & Padrões • Bloco 2: SOLID & GRASP • Demonstrações Práticas

🏛️ Desacoplamento e Injeção de Dependências na Prática: Demonstrações funcionais dos princípios ISP e DIP. No Exemplo 1, aplicamos a Segregação de Interfaces em um sistema de processamento fiscal e contábil. No Exemplo 2, construímos um mini-container IoC (Inversion of Control Container com Autowiring) em Python que resolve automaticamente o grafo de dependências via introspecção de tipos.


1. Segregação de Interfaces (ISP) em Pipeline Documental

Neste exemplo, eliminamos uma interface volumosa (Fat Interface) decompondo-a em interfaces de papel (Role Interfaces) coesas. Cada cliente consome apenas o subconjunto de métodos estritamente necessários à sua operação:

# solid_isp_documentos.py
from abc import ABC, abstractmethod
from typing import List

# 1. Interfaces Granulares de Papel (Role Interfaces - 100% ISP Conforme)
class ILeitorDocumento(ABC):
    @abstractmethod
    def ler_conteudo(self, caminho_arquivo: str) -> str:
        pass

class IGeradorPDF(ABC):
    @abstractmethod
    def exportar_pdf(self, conteudo: str, caminho_destino: str) -> bool:
        pass

class IAssinadorDigital(ABC):
    @abstractmethod
    def assinar_criptograficamente(self, payload: str, certificado_pem: str) -> str:
        pass

class ITransmissorSEFAZ(ABC):
    @abstractmethod
    def transmitir_lote(self, xml_assinado: str) -> str:
        pass

# 2. Implementações Concretas com Habilidades Específicas
class DispositivoVisualizadorSimples(ILeitorDocumento):
    """Apenas lê documentos para exibição na tela. Não gera PDF nem assina nada."""
    def ler_conteudo(self, caminho_arquivo: str) -> str:
        print(f"  📖 [Visualizador] Carregando bytes de: {caminho_arquivo}")
        return "<xml><fatura valor='1500.00' /></xml>"

class ServicoEmissaoFiscalAvancado(ILeitorDocumento, IAssinadorDigital, ITransmissorSEFAZ):
    """Serviço corporativo que orquestra leitura, assinatura com certificado A1 e transmissão."""
    def ler_conteudo(self, caminho_arquivo: str) -> str:
        print(f"  📂 [Fiscal] Lendo XML de fatura: {caminho_arquivo}")
        return "<xml><nota id='4501' total='9800.00' /></xml>"

    def assinar_criptograficamente(self, payload: str, certificado_pem: str) -> str:
        print(f"  🔐 [Criptografia] Assinando payload com certificado RSA SHA-256...")
        return f"{payload}<!-- ASSINATURA_DIGITAL_RSA_VALIDA -->"

    def transmitir_lote(self, xml_assinado: str) -> str:
        print(f"  📡 [SEFAZ] Transmitindo lote SOAP via mTLS para a Receita Federal...")
        return "RECIBO_SEFAZ_AUTORIZADO_2026_9988"

# 3. Consumidores Específicos: Acoplados APENAS ao que utilizam
def rotina_auditoria_leitura(leitor: ILeitorDocumento, caminho: str):
    print("\n[Audit Routine] Verificando integridade de arquivo:")
    conteudo = leitor.ler_conteudo(caminho)
    print(f"  Tamanho do conteúdo lido: {len(conteudo)} caracteres.")

def rotina_transmissao_governamental(assinador: IAssinadorDigital, transmissor: ITransmissorSEFAZ, xml: str):
    print("\n[Gov Transmit Routine] Executando protocolo oficial:")
    xml_assinado = assinador.assinar_criptograficamente(xml, "cert_a1.pem")
    protocolo = transmissor.transmitir_lote(xml_assinado)
    print(f"  ✅ Transmissão homologada com protocolo: {protocolo}")

# Demonstração Prática
if __name__ == "__main__":
    visualizador = DispositivoVisualizadorSimples()
    servico_fiscal = ServicoEmissaoFiscalAvancado()

    print("=" * 65)
    print("✂️ DEMONSTRAÇÃO DO PRINCÍPIO DE SEGREGAÇÃO DE INTERFACES (ISP)")
    print("=" * 65)

    # O visualizador simples é aceito pela rotina que só exige leitura
    rotina_auditoria_leitura(visualizador, "/tmp/fatura_01.xml")

    # O serviço fiscal atende a ambos os contratos sem poluição de métodos
    rotina_auditoria_leitura(servico_fiscal, "/var/notas/nfe_4501.xml")
    rotina_transmissao_governamental(servico_fiscal, servico_fiscal, "<nfe valor='9800'/>")

2. Container IoC com Resolução Automática de Dependências (Autowiring)

Neste exemplo, implementamos do zero um container de Inversão de Controle (IoC Container) que inspeciona assinaturas de tipo e resolve todo o grafo de dependências via injeção por construtor:

# solid_dip_ioc_container.py
import inspect
from abc import ABC, abstractmethod
from typing import Dict, Type, Any, get_type_hints

# 1. Abstrações de Domínio (DIP - Núcleo de Alto Nível)
class ILogger(ABC):
    @abstractmethod
    def log(self, mensagem: str): pass

class IGatewayPagamento(ABC):
    @abstractmethod
    def cobrar(self, cliente: str, valor: float) -> bool: pass

class INotificadorSMS(ABC):
    @abstractmethod
    def notificar(self, telefone: str, texto: str): pass

# 2. Implementações Técnicas de Baixo Nível
class ConsoleLogger(ILogger):
    def log(self, mensagem: str):
        print(f"  📝 [LOG DE AUDITORIA] {mensagem}")

class StripeGatewayPagamento(IGatewayPagamento):
    def __init__(self, logger: ILogger):
        self.logger = logger

    def cobrar(self, cliente: str, valor: float) -> bool:
        self.logger.log(f"Conectando à API REST do Stripe para debitar R$ {valor:.2f} do cliente '{cliente}'...")
        return True

class TwilioNotificadorSMS(INotificadorSMS):
    def __init__(self, logger: ILogger):
        self.logger = logger

    def notificar(self, telefone: str, texto: str):
        self.logger.log(f"Disparando SMS via Twilio Gateway para {telefone}: '{texto}'")

# 3. Módulo de Alto Nível de Negócio (Checkout)
class CheckoutPedidoService:
    """Depende EXCLUSIVAMENTE de abstrações fornecidas pelo construtor."""
    def __init__(self, gateway: IGatewayPagamento, sms: INotificadorSMS, logger: ILogger):
        self.gateway = gateway
        self.sms = sms
        self.logger = logger

    def finalizar_compra(self, pedido_id: str, cliente: str, telefone: str, valor: float):
        self.logger.log(f"Iniciando checkout do pedido #{pedido_id}")
        sucesso = self.gateway.cobrar(cliente, valor)
        if sucesso:
            self.sms.notificar(telefone, f"Seu pedido #{pedido_id} de R$ {valor:.2f} foi aprovado!")
            self.logger.log(f"Pedido #{pedido_id} concluído com êxito total.")

# 4. Mecanismo de IoC Container com Injeção por Autowiring
class IoCContainer:
    def __init__(self):
        self._servicos: Dict[Type, Any] = {}
        self._singletons: Dict[Type, Any] = {}

    def registrar_singleton(self, interface: Type, implementacao: Type):
        """Registra uma implementação no escopo Singleton (compartilhada)."""
        self._servicos[interface] = (implementacao, "SINGLETON")

    def registrar_transient(self, interface: Type, implementacao: Type):
        """Registra uma implementação no escopo Transient (nova instância a cada resolução)."""
        self._servicos[interface] = (implementacao, "TRANSIENT")

    def resolver(self, tipo_alvo: Type) -> Any:
        """Resolve recursivamente as dependências do construtor inspecionando tipagem."""
        if tipo_alvo in self._servicos:
            impl_classe, escopo = self._servicos[tipo_alvo]
            if escopo == "SINGLETON" and tipo_alvo in self._singletons:
                return self._singletons[tipo_alvo]
        else:
            impl_classe = tipo_alvo
            escopo = "TRANSIENT"

        # Inspecionar construtor
        assinatura = inspect.signature(impl_classe.__init__)
        tipos = get_type_hints(impl_classe.__init__)
        argumentos = {}

        for nome_param, param in assinatura.parameters.items():
            if nome_param == "self":
                continue
            tipo_param = tipos.get(nome_param)
            if tipo_param is None:
                raise TypeError(f"Não foi possível inferir tipo do parâmetro '{nome_param}' em {impl_classe}")
            # Resolução recursiva de dependência
            argumentos[nome_param] = self.resolver(tipo_param)

        instancia = impl_classe(**argumentos)
        if tipo_alvo in self._servicos and self._servicos[tipo_alvo][1] == "SINGLETON":
            self._singletons[tipo_alvo] = instancia
        return instancia

# 5. Composition Root (Ponto Único de Inicialização)
if __name__ == "__main__":
    print("\n" + "=" * 65)
    print("🔄 RESOLUÇÃO DE DEPENDÊNCIAS VIA CONTAINER IoC (DIP PLENO)")
    print("=" * 65)

    container = IoCContainer()
    # Amarração de abstrações com classes concretas
    container.registrar_singleton(ILogger, ConsoleLogger)
    container.registrar_transient(IGatewayPagamento, StripeGatewayPagamento)
    container.registrar_transient(INotificadorSMS, TwilioNotificadorSMS)

    # O container descobre e monta todo o grafo de objetos automaticamente!
    checkout_app = container.resolver(CheckoutPedidoService)

    # Execução do fluxo de negócio sem acoplamento técnico
    checkout_app.finalizar_compra(
        pedido_id="PED-7788",
        cliente="Mariana Guimarães",
        telefone="+5511988887777",
        valor=1450.00
    )

Saída Esperada dos Scripts:

=================================================================
✂️ DEMONSTRAÇÃO DO PRINCÍPIO DE SEGREGAÇÃO DE INTERFACES (ISP)
=================================================================

[Audit Routine] Verificando integridade de arquivo:
  📖 [Visualizador] Carregando bytes de: /tmp/fatura_01.xml
  Tamanho do conteúdo lido: 34 caracteres.

[Audit Routine] Verificando integridade de arquivo:
  📂 [Fiscal] Lendo XML de fatura: /var/notas/nfe_4501.xml
  Tamanho do conteúdo lido: 42 caracteres.

[Gov Transmit Routine] Executando protocolo oficial:
  🔐 [Criptografia] Assinando payload com certificado RSA SHA-256...
  📡 [SEFAZ] Transmitindo lote SOAP via mTLS para a Receita Federal...
  ✅ Transmissão homologada com protocolo: RECIBO_SEFAZ_AUTORIZADO_2026_9988

=================================================================
🔄 RESOLUÇÃO DE DEPENDÊNCIAS VIA CONTAINER IoC (DIP PLENO)
=================================================================
  📝 [LOG DE AUDITORIA] Iniciando checkout do pedido #PED-7788
  📝 [LOG DE AUDITORIA] Conectando à API REST do Stripe para debitar R$ 1450.00 do cliente 'Mariana Guimarães'...
  📝 [LOG DE AUDITORIA] Disparando SMS via Twilio Gateway para +5511988887777: 'Seu pedido #PED-7788 de R$ 1450.00 foi aprovado!'
  📝 [LOG DE AUDITORIA] Pedido #PED-7788 concluído com êxito total.