🧬 CAPÍTULO 13: HERANÇA, POLIMORFISMO E ASSOCIAÇÕES


🎯 1. Objetivos de Aprendizagem & Competências

Estimativa de Dedicação: 2 horas de estudo autoguiado.
Ao final deste capítulo, você será capaz de:

  • 🔹 Diferenciar e aplicar os 4 relacionamentos essenciais da UML: Associação Simples, Agregação (losango vazio), Composição (losango preenchido) e Generalização/Herança (triângulo vazio).
  • 🔹 Distinguir Agregação (relação fraca 'tem-um', ciclo de vida independente) de Composição (relação forte 'parte-todo', ciclo de vida dependente e exclusão em cascata).
  • 🔹 Aplicar Polimorfismo e Classes Abstratas (abc.ABC) para garantir desacoplamento e extensibilidade.
  • 🔹 Implementar relacionamentos no SQLAlchemy 2.0 com relationship() e cascade="all, delete-orphan".

🏢 2. Cenário Corporativo & Estudo de Caso (TecProExpress)

Na TecProExpress, a plataforma de e-commerce e faturamento processa pedidos compostos por múltiplos itens. Se um Pedido for cancelado ou excluído do banco de dados, todos os seus ItensPedido associados devem ser automaticamente eliminados (Composição). No entanto, o Produto do catálogo continua existindo para outras vendas (Agregação).

O Desafio: Como Arquiteto de Software, você deve modelar as relações entre as classes com as cardinalidades corretas, garantindo que o ciclo de vida dos objetos reflita as regras de negócio e a integridade referencial do banco de dados.


🧠 3. Fundamentação Teórica & Modelos Visuais

3.1. Panorama dos Relacionamentos entre Classes na UML

classDiagram
    class Pedido {
        +int idPedido
        +date dataEmissao
        +calcularTotal() Decimal
    }
    class ItemPedido {
        +int quantidade
        +Decimal precoUnitario
        +subtotal() Decimal
    }
    class Produto {
        +int idProduto
        +string nome
        +Decimal preco
    }
    class Cliente {
        +int idCliente
        +string nome
    }
    
    Cliente "1" --> "0..*" Pedido : Associação
    Pedido "1" *-- "1..*" ItemPedido : Composição (Todo-Parte Forte)
    ItemPedido "0..*" o-- "1" Produto : Agregação (Parte Fraca)

3.2. Comparativo: Agregação vs. Composição vs. Herança

RelacionamentoSímbolo UMLCiclo de VidaExemplo Prático
AssociaçãoSeta Simples -->Independente; uma classe utiliza métodos ou dados de outra.Motorista conduz Caminhão.
AgregaçãoLosango Vazio o--Fraco ('tem-um'); a parte pode existir sem o todo.Departamento agrega Professores.
ComposiçãoLosango Cheio *--Forte ('parte-todo'); a parte morre junto com o todo.NotaFiscal compõe ItensNotaFiscal.
Herança / GeneralizaçãoTriângulo Vazio `<--`Compartilhamento de atributos e métodos da superclasse ('é-um').

💻 4. Aplicação Prática & Código Executável (Python 3.11+)

📋 Pré-requisitos e Instalação

Este exemplo utiliza os módulos padrão abc, decimal e dataclasses, sem necessidade de pacotes externos:

python --version  # Requer Python 3.11 ou superior

💻 Código Completo e Autocontido (modelo_pedido_composicao.py)

Implementação de Composição Forte e Polimorfismo de Estratégia de Desconto em Python 3.11+:

"""
Módulo: modelo_pedido_composicao.py
Domínio: Composição forte e polimorfismo de regras de frete e desconto.
"""
from abc import ABC, abstractmethod
from decimal import Decimal
from dataclasses import dataclass

# --- POLIMORFISMO DE ESTRATÉGIA DE DESCONTO ---
class RegraDesconto(ABC):
    @abstractmethod
    def calcular_desconto(self, valor_bruto: Decimal) -> Decimal:
        pass

class DescontoClientePadrao(RegraDesconto):
    def calcular_desconto(self, valor_bruto: Decimal) -> Decimal:
        return Decimal("0.00")

class DescontoClienteVIP(RegraDesconto):
    def calcular_desconto(self, valor_bruto: Decimal) -> Decimal:
        return valor_bruto * Decimal("0.10") # 10% OFF

# --- COMPOSIÇÃO: Pedido e ItemPedido ---
@dataclass
class ItemPedido:
    descricao: str
    quantidade: int
    preco_unitario: Decimal

    def subtotal(self) -> Decimal:
        return self.quantidade * self.preco_unitario

class Pedido:
    def __init__(self, id_pedido: int, estrategia_desconto: RegraDesconto):
        self.id_pedido = id_pedido
        self.estrategia_desconto = estrategia_desconto
        self.itens: list[ItemPedido] = [] # Composição: itens pertencem ao ciclo deste pedido

    def adicionar_item(self, descricao: str, quantidade: int, preco: Decimal):
        self.itens.append(ItemPedido(descricao, quantidade, preco))

    def calcular_total_bruto(self) -> Decimal:
        return sum((item.subtotal() for item in self.itens), Decimal("0.00"))

    def calcular_total_liquido(self) -> Decimal:
        bruto = self.calcular_total_bruto()
        desconto = self.estrategia_desconto.calcular_desconto(bruto)
        return bruto - desconto

if __name__ == "__main__":
    print("--- Simulação de Pedidos com Composição e Polimorfismo ---")
    
    # Pedido 1: Cliente Padrão (Sem Desconto)
    pedido_padrao = Pedido(1001, DescontoClientePadrao())
    pedido_padrao.adicionar_item("Teclado Mecânico", 1, Decimal("350.00"))
    print(f"📦 Pedido #{pedido_padrao.id_pedido} (Padrão):")
    print(f"   Bruto: R$ {pedido_padrao.calcular_total_bruto():.2f} | Líquido: R$ {pedido_padrao.calcular_total_liquido():.2f}")

    # Pedido 2: Cliente VIP (10% OFF)
    pedido_vip = Pedido(1002, DescontoClienteVIP())
    pedido_vip.adicionar_item("Monitor Dell 27", 2, Decimal("1200.00"))
    pedido_vip.adicionar_item("Teclado Mecânico", 1, Decimal("350.00"))
    print(f"🌟 Pedido #{pedido_vip.id_pedido} (VIP 10% OFF):")
    print(f"   Bruto: R$ {pedido_vip.calcular_total_bruto():.2f} | Líquido: R$ {pedido_vip.calcular_total_liquido():.2f}")

🚀 Como Executar

Execute o script diretamente no terminal:

python modelo_pedido_composicao.py

🖥️ Saída Esperada no Terminal

--- Simulação de Pedidos com Composição e Polimorfismo ---
📦 Pedido #1001 (Padrão):
   Bruto: R$ 350.00 | Líquido: R$ 350.00
🌟 Pedido #1002 (VIP 10% OFF):
   Bruto: R$ 2750.00 | Líquido: R$ 2475.00

💡 5. Checkpoint de Engenharia & Boas Práticas

Boas Práticas & Anti-Patterns

  • Prefira Composição à Herança (Gang of Four): A herança cria acoplamento rígido entre superclasse e subclasse. Sempre que possível, monte comportamentos combinando objetos menores através de composição e interfaces polimórficas.
  • Anti-Pattern Cascading Delete Acidental: Em relacionamentos de Agregação, nunca marque cascade="all, delete-orphan", pois isso apagará entidades compartilhadas acidentalmente.

🔗 6. Conexão com os Projetos Integradores

Projeto IntegradorComo o conceito deste capítulo é aplicado no PI
PI-04: ShopFlowComposição PedidoItemPedido com exclusão em cascata no SQLAlchemy ORM.
PI-08: CattleFlowAgregação entre PastoPiquete e AnimalBovino (o animal é rotacionado de pasto sem ser excluído).

🧪 7. Quiz de Fixação e Autoavaliação (Formative Assessment)

🧪 Quiz de Autoavaliação — Capítulo 13

1. Qual é a principal diferença estrutural entre 'Agregação' e 'Composição' na UML?

  • A) Agregação usa losango preto e Composição usa triângulo.
  • B) Na Composição, a parte possui dependência existencial do todo (se o todo for destruído, as partes deixam de existir); na Agregação, as partes podem existir de forma independente do todo.
  • C) Na Agregação, a parte não pode pertencer a nenhuma outra classe.
  • D) Não há diferença prática; são termos intercambiáveis na UML 2.5.
💡 Ver Resposta e Justificativa

Resposta Correta: B
Justificativa: A Composição representa um vínculo 'todo-parte' forte com ciclo de vida coincidente (ex: Nota Fiscal e seus Itens). A Agregação é um vínculo fraco onde as partes sobrevivem à destruição do objeto agregador (ex: Time e Jogadores).


2. No diagrama de classes da UML, o símbolo gráfico do relacionamento de 'Composição' é:

  • A) Uma linha pontilhada com seta aberta.
  • B) Um losango preenchido (losango preto/sólido) no lado do objeto 'Todo'.
  • C) Um triângulo vazio no lado da superclasse.
  • D) Um círculo com uma cruz dentro.
💡 Ver Resposta e Justificativa

Resposta Correta: B
Justificativa: O losango sólido (preenchido) indica Composição forte no lado da classe que atua como proprietária do ciclo de vida das partes.


3. O princípio da POO 'Polimorfismo' permite:

  • A) Gravar dados em arquivos texto sem formatação.
  • B) Tratar objetos de diferentes classes derivadas através de uma mesma interface comum, executando comportamentos específicos em tempo de execução.
  • C) Impedir o uso de funções assíncronas.
  • D) Criar tabelas sem chaves primárias.
💡 Ver Resposta e Justificativa

Resposta Correta: B
Justificativa: O polimorfismo possibilita invocar o mesmo método em objetos de diferentes tipos, com cada objeto respondendo de acordo com sua própria implementação especializada.


🛠️ 8. Ponte para a Ação: Laboratório Prático

🎯 Próximo Passo Prático

Coloque esta teoria em prática executando o roteiro de laboratório autoguiado:
👉 ATIVIDADE 07: DIAGRAMA DE CLASSES (UML)


📌 9. Resumo Executivo & Key Takeaways

  • Associação: Relação genérica de conhecimento entre duas classes.
  • Agregação (Losango Vazio): Vínculo fraco; as partes continuam existindo de forma independente.
  • Composição (Losango Cheio): Vínculo forte de posse existencial; exclusão em cascata obrigatória.
  • Polimorfismo: Desacoplamento através de classes base abstratas (abc.ABC), permitindo trocar algoritmos sem quebrar os clientes.