Especialização em Sistemas com Python 3.12+ • Capítulo 05


🎯 Demonstração Prática: Gestão de Contas Corporativas com Slots, Properties e Factory Methods

O exemplo abaixo demonstra a modelagem orientada a objetos de alta performance e encapsulamento estrito:

  • Utilização de __slots__ para otimização de memória em nível de PVM.
  • Propriedades com validação defensiva através de @property e setters de invariantes.
  • Método de fábrica polimórfico @classmethod com typing.Self (PEP 673).
  • Validador algorítmico utilitário com @staticmethod.
  • Métodos dunder de representação formal __repr__ e legível __str__.
"""
demonstracao_poo_encapsulamento.py
Conta Bancária Corporativa de Alta Performance com Slots, Property e Factory Methods.
Compatível com Python 3.12+ (PEP 673 typing.Self e Type Hints Estritos).
"""

from __future__ import annotations

from decimal import Decimal, ROUND_HALF_EVEN
import json
import re
from typing import Final, Self

# ==============================================================================
# MODELO DE DOMÍNIO COM ENCAPSULAMENTO E SLOTS
# ==============================================================================
class ContaEmpresarial:
    """
    Modela uma conta corporativa com slots estáticos para redução de pegada na Heap.
    Não aloca __dict__ por instância.
    """
    __slots__ = ("_numero_conta", "_razao_social", "_saldo", "_limite_cheque_especial", "_ativa")

    def __init__(
        self,
        numero_conta: str,
        razao_social: str,
        saldo_inicial: Decimal = Decimal("0.00"),
        limite_credito: Decimal = Decimal("5000.00")
    ) -> None:
        if not self.validar_formato_conta(numero_conta):
            raise ValueError(f"Formato de conta inválido: '{numero_conta}'. Padrão esperado: 'XXXXX-X'.")

        self._numero_conta: str = numero_conta
        self._razao_social: str = razao_social.strip()
        self._saldo: Decimal = Decimal("0.00")
        self._limite_cheque_especial: Decimal = Decimal("0.00")
        self._ativa: bool = True

        # Atribuição passando pelos setters para garantir invariantes
        self.limite_cheque_especial = limite_credito
        if saldo_inicial > Decimal("0.00"):
            self.depositar(saldo_inicial)

    # --------------------------------------------------------------------------
    # PROPRIEDADES E ENCAPSULAMENTO
    # --------------------------------------------------------------------------
    @property
    def numero_conta(self) -> str:
        """Acesso somente-leitura ao identificador da conta."""
        return self._numero_conta

    @property
    def razao_social(self) -> str:
        return self._razao_social

    @property
    def saldo(self) -> Decimal:
        return self._saldo

    @property
    def saldo_total_disponivel(self) -> Decimal:
        """Propriedade computada somando saldo em conta e limite de crédito aprovado."""
        return self._saldo + self._limite_cheque_especial

    @property
    def limite_cheque_especial(self) -> Decimal:
        return self._limite_cheque_especial

    @limite_cheque_especial.setter
    def limite_cheque_especial(self, novo_limite: Decimal) -> None:
        """Setter defensivo com validação de limite mínimo."""
        if novo_limite < Decimal("0.00"):
            raise ValueError(f"O limite de crédito não pode ser negativo: {novo_limite}")
        self._limite_cheque_especial = novo_limite.quantize(Decimal("0.01"), ROUND_HALF_EVEN)

    @property
    def ativa(self) -> bool:
        return self._ativa

    # --------------------------------------------------------------------------
    # COMPORTAMENTOS TRANSACIONAIS
    # --------------------------------------------------------------------------
    def depositar(self, valor: Decimal) -> None:
        """Realiza crédito em conta com validação de valor estritamente positivo."""
        if not self._ativa:
            raise RuntimeError("Operação rejeitada: Conta bancária inativa.")
        if valor <= Decimal("0.00"):
            raise ValueError(f"Valor de depósito deve ser positivo: {valor}")
        
        self._saldo = (self._saldo + valor).quantize(Decimal("0.01"), ROUND_HALF_EVEN)

    def sacar(self, valor: Decimal) -> bool:
        """Efetua débito verificando disponibilidade total (saldo + limite)."""
        if not self._ativa:
            raise RuntimeError("Operação rejeitada: Conta bancária inativa.")
        if valor <= Decimal("0.00"):
            raise ValueError(f"Valor de saque deve ser positivo: {valor}")

        if valor > self.saldo_total_disponivel:
            return False  # Fundos insuficientes

        self._saldo = (self._saldo - valor).quantize(Decimal("0.01"), ROUND_HALF_EVEN)
        return True

    # --------------------------------------------------------------------------
    # MÉTODOS DE FÁBRICA (@classmethod) E UTILITÁRIOS (@staticmethod)
    # --------------------------------------------------------------------------
    @classmethod
    def from_json(cls, json_str: str) -> Self:
        """Factory Method polimórfico utilizando typing.Self para criar instâncias via JSON."""
        dados = json.loads(json_str)
        return cls(
            numero_conta=str(dados["numero_conta"]),
            razao_social=str(dados["razao_social"]),
            saldo_inicial=Decimal(str(dados.get("saldo", "0.00"))),
            limite_credito=Decimal(str(dados.get("limite", "5000.00")))
        )

    @staticmethod
    def validar_formato_conta(numero: str) -> bool:
        """Valida se o identificador segue a máscara de 5 dígitos + hífen + 1 dígito verificador."""
        return bool(re.match(r"^\d{5}-\d{1}$", numero.strip()))

    # --------------------------------------------------------------------------
    # DUNDER METHODS DE REPRESENTAÇÃO
    # --------------------------------------------------------------------------
    def __repr__(self) -> str:
        return (
            f"ContaEmpresarial(numero_conta='{self._numero_conta}', "
            f"razao_social='{self._razao_social}', saldo={self._saldo}, "
            f"limite={self._limite_cheque_especial})"
        )

    def __str__(self) -> str:
        return f"Conta {self._numero_conta} - {self._razao_social} | Saldo: R$ {self._saldo:,.2f}"

# ==============================================================================
# EXECUÇÃO E TESTES
# ==============================================================================
if __name__ == "__main__":
    payload_integracao = """
    {
        "numero_conta": "98124-7",
        "razao_social": "TechCorp Inovacoes Digitais Ltda",
        "saldo": "14500.50",
        "limite": "20000.00"
    }
    """

    print("=" * 75)
    print("      SISTEMA BANCÁRIO ORIENTADO A OBJETOS COM __SLOTS__")
    print("=" * 75)

    conta_pj: ContaEmpresarial = ContaEmpresarial.from_json(payload_integracao)

    print(f"Instância Criada : {conta_pj}")
    print(f"Representação    : {repr(conta_pj)}")
    print(f"Saldo Próprio    : R$ {conta_pj.saldo:>12.2f}")
    print(f"Limite Aprovado  : R$ {conta_pj.limite_cheque_especial:>12.2f}")
    print(f"Poder de Compra  : R$ {conta_pj.saldo_total_disponivel:>12.2f}")
    print("-" * 75)

    print(">> Realizando saque de R$ 25.000,00 (Entrando no limite de crédito)...")
    sucesso_saque = conta_pj.sacar(Decimal("25000.00"))
    print(f"Saque Aprovado?  : {sucesso_saque}")
    print(f"Novo Saldo       : R$ {conta_pj.saldo:>12.2f} (Utilizando cheque especial)")
    print(f"Disponível Rest. : R$ {conta_pj.saldo_total_disponivel:>12.2f}")

    print("-" * 75)
    print(">> Tentativa de saque de R$ 15.000,00 (Estouro de limite)...")
    sucesso_estouro = conta_pj.sacar(Decimal("15000.00"))
    print(f"Saque Aprovado?  : {sucesso_estouro} (Rejeitado por fundos insuficientes)")
    print("=" * 75)