Módulo 10: Paradigmas & Padrões • Bloco 2: SOLID & GRASP • Trilha em 4 Níveis


🎯 Nível 1: Fundamentos

Problema 07.1 — Information Expert: Totalizador de Carrinho

Contexto: O padrão Information Expert dita que quem possui os dados deve ter o comportamento de cálculo sobre eles.

Requisitos de Execução:

  1. Crie a classe ItemCarrinho com produto, quantidade e preco_unitario e o método calcular_subtotal().
  2. Crie a classe CarrinhoDeCompras que agrega os itens e calcula o total geral delegando os subtotais para cada item.

Resultado Esperado

=== Carrinho de Compras (Information Expert) ===
Item: Mouse Óptico (2x R$ 80.00) -> Subtotal: R$ 160.00
Item: Teclado Mecânico (1x R$ 320.00) -> Subtotal: R$ 320.00
Total Consolidado do Carrinho: R$ 480.00

📤 Instruções de Entrega (Microsoft Teams)

  1. Salve o arquivo como: Atividade_07_1_SeuNome.py
  2. Envie na tarefa: Atividade Cap 07 - Padrões GRASP Fundamentais
🔑 Gabarito de Código & Solução Comentada
# Atividade_07_1_Gabarito.py
from typing import List

class ItemCarrinho:
    def __init__(self, produto: str, quantidade: int, preco_unitario: float):
        self.produto = produto
        self.quantidade = quantidade
        self.preco_unitario = preco_unitario

    def calcular_subtotal(self) -> float:
        return self.quantidade * self.preco_unitario

class CarrinhoDeCompras:
    def __init__(self):
        self._itens: List[ItemCarrinho] = []

    def adicionar_item(self, item: ItemCarrinho):
        self._itens.append(item)

    def calcular_total(self) -> float:
        return sum(item.calcular_subtotal() for item in self._itens)

    def exibir_resumo(self):
        print("=== Carrinho de Compras (Information Expert) ===")
        for it in self._itens:
            print(f"Item: {it.produto} ({it.quantidade}x R$ {it.preco_unitario:.2f}) -> Subtotal: R$ {it.calcular_subtotal():.2f}")
        print(f"Total Consolidado do Carrinho: R$ {self.calcular_total():.2f}")

if __name__ == "__main__":
    carrinho = CarrinhoDeCompras()
    carrinho.adicionar_item(ItemCarrinho("Mouse Óptico", 2, 80.00))
    carrinho.adicionar_item(ItemCarrinho("Teclado Mecânico", 1, 320.00))
    carrinho.exibir_resumo()

🔍 Nível 2: Prática

Problema 07.2 — Padrão Creator e Agregação de Pedidos

Contexto: O padrão Creator orienta que a classe mãe agregadora deve ter a responsabilidade de instanciar suas entidades filhas.

Requisitos de Execução:

  1. A classe OrdemDeServico deve ser a única responsável por criar instâncias de ApontamentoHoras.
  2. Implemente o método ordem.registrar_apontamento(desenvolvedor: str, horas: float, valor_hora: float).
  3. Garanta que o cliente da API não instancie ApontamentoHoras diretamente com new/construtor.

Resultado Esperado

=== Ordem de Serviço #OS-2026-901 ===
[Creator] Apontamento registrado: Lucas (4.0h @ R$ 120.00/h) -> R$ 480.00
[Creator] Apontamento registrado: Carla (6.5h @ R$ 150.00/h) -> R$ 975.00
Custo Total da OS: R$ 1455.00

📤 Instruções de Entrega (Microsoft Teams)

  1. Salve o arquivo como: Atividade_07_2_SeuNome.py
  2. Envie na tarefa: Atividade Cap 07 - Padrões GRASP Fundamentais
🔑 Gabarito de Código & Solução Comentada
# Atividade_07_2_Gabarito.py
from typing import List

class ApontamentoHoras:
    def __init__(self, dev: str, horas: float, valor_hora: float):
        self.dev = dev
        self.horas = horas
        self.valor_hora = valor_hora

    def calcular_custo(self) -> float:
        return self.horas * self.valor_hora

class OrdemDeServico:
    def __init__(self, numero_os: str):
        self.numero_os = numero_os
        self._apontamentos: List[ApontamentoHoras] = []

    # Creator: Cria e agrega a entidade interna
    def registrar_apontamento(self, dev: str, horas: float, valor_hora: float) -> ApontamentoHoras:
        ap = ApontamentoHoras(dev, horas, valor_hora)
        self._apontamentos.append(ap)
        print(f"[Creator] Apontamento registrado: {dev} ({horas}h @ R$ {valor_hora:.2f}/h) -> R$ {ap.calcular_custo():.2f}")
        return ap

    def calcular_custo_total(self) -> float:
        return sum(ap.calcular_custo() for ap in self._apontamentos)

if __name__ == "__main__":
    os = OrdemDeServico("OS-2026-901")
    print(f"=== Ordem de Serviço #{os.numero_os} ===")
    os.registrar_apontamento("Lucas", 4.0, 120.00)
    os.registrar_apontamento("Carla", 6.5, 150.00)
    print(f"Custo Total da OS: R$ {os.calcular_custo_total():.2f}")

🚀 Nível 3: Integração

Problema 07.3 — Padrão Controller (Façade do Caso de Uso)

Contexto: O padrão Controller isola a camada visual dos objetos de negócio, orquestrando as operações do caso de uso.

Requisitos de Execução:

  1. Crie o MatriculaCursoController.
  2. O controlador deve receber requisições de matrícula, validar pré-requisitos no AlunoDomainService, registrar no TurmaRepository e disparar o recibo via FinanceiroService.
  3. Garanta que a UI interaja exclusivamente com o Controller.

Resultado Esperado

=== MatriculaController (GRASP Controller) ===
[Controller] Recebida solicitação: Aluno #101 na Turma #COMP-10
[Domain] Pré-requisitos validados com sucesso!
[Turma] Aluno matriculado. Vagas restantes: 14/15
[Financeiro] Boleto da taxa de matrícula gerado: R$ 350.00
Status da Operação: SUCESSO

📤 Instruções de Entrega (Microsoft Teams)

  1. Salve o arquivo como: Atividade_07_3_SeuNome.py
  2. Envie na tarefa: Atividade Cap 07 - Padrões GRASP Fundamentais
🔑 Gabarito de Código & Solução Comentada
# Atividade_07_3_Gabarito.py

class AlunoDomainService:
    def validar_pre_requisitos(self, aluno_id: int) -> bool:
        print("[Domain] Pré-requisitos validados com sucesso!")
        return True

class TurmaRepository:
    def matricular(self, aluno_id: int, turma_id: str):
        print(f"[Turma] Aluno matriculado. Vagas restantes: 14/15")

class FinanceiroService:
    def gerar_cobranca(self, aluno_id: int, valor: float):
        print(f"[Financeiro] Boleto da taxa de matrícula gerado: R$ {valor:.2f}")

class MatriculaCursoController:
    def __init__(self, domain: AlunoDomainService, repo: TurmaRepository, fin: FinanceiroService):
        self._domain = domain
        self._repo = repo
        self._fin = fin

    def processar_matricula(self, aluno_id: int, turma_id: str) -> bool:
        print(f"[Controller] Recebida solicitação: Aluno #{aluno_id} na Turma #{turma_id}")
        if self._domain.validar_pre_requisitos(aluno_id):
            self._repo.matricular(aluno_id, turma_id)
            self._fin.gerar_cobranca(aluno_id, 350.00)
            return True
        return False

if __name__ == "__main__":
    print("=== MatriculaController (GRASP Controller) ===")
    ctrl = MatriculaCursoController(AlunoDomainService(), TurmaRepository(), FinanceiroService())
    ok = ctrl.processar_matricula(101, "COMP-10")
    print(f"Status da Operação: {'SUCESSO' if ok else 'FALHA'}")

🏆 Nível 4: Desafio

Problema 07.4 — Arquitetura de Alto Coesão e Baixo Acoplamento para Processamento de Sinistros

Contexto: Seguradoras processam sinistros de automóveis com dezenas de etapas (perícia, cobertura, franquia, indenização).

Requisitos de Execução:

  1. Aplique Information Expert: O ContratoSeguro calcula o valor da indenização com base na tabela FIPE e franquia contratada.
  2. Aplique Creator: O Sinistro instancia e registra os LaudoPericial.
  3. Aplique Controller: O SinistroProcessamentoController orquestra a esteira completa garantindo Alta Coesão e Baixo Acoplamento.

Resultado Esperado

=== Esteira de Sinistro Automotivo (GRASP Completo) ===
[Sinistro #SIN-8812] Criado para Veículo Placa 'ABC-1234'
[Creator] Laudo registrado: Perda Parcial (Reparo: R$ 8500.00)
[Information Expert] Franquia contratada: R$ 2000.00 | Cobertura da Seguradora: R$ 6500.00
[Controller] Sinistro liquidado e enviado para pagamento da oficina!

📤 Instruções de Entrega (Microsoft Teams)

  1. Salve o arquivo como: Atividade_07_4_SeuNome.py
  2. Envie na tarefa: Atividade Cap 07 - Padrões GRASP Fundamentais
🔑 Gabarito de Código & Solução Comentada
# Atividade_07_4_Gabarito.py
from typing import List

class LaudoPericial:
    def __init__(self, tipo_dano: str, custo_reparo: float):
        self.tipo_dano = tipo_dano
        self.custo_reparo = custo_reparo

class Sinistro:
    def __init__(self, id_sinistro: str, placa: str, franquia: float):
        self.id_sinistro = id_sinistro
        self.placa = placa
        self.franquia = franquia
        self._laudos: List[LaudoPericial] = []

    # Creator
    def adicionar_laudo(self, tipo: str, custo: float) -> LaudoPericial:
        laudo = LaudoPericial(tipo, custo)
        self._laudos.append(laudo)
        print(f"[Creator] Laudo registrado: {tipo} (Reparo: R$ {custo:.2f})")
        return laudo

    # Information Expert
    def calcular_indenizacao(self) -> float:
        total_reparo = sum(l.custo_reparo for l in self._laudos)
        cobertura = max(0.0, total_reparo - self.franquia)
        print(f"[Information Expert] Franquia contratada: R$ {self.franquia:.2f} | Cobertura da Seguradora: R$ {cobertura:.2f}")
        return cobertura

class SinistroProcessamentoController:
    def processar_esteira(self, id_sinistro: str, placa: str, franquia: float, reparo: float):
        print(f"[Sinistro #{id_sinistro}] Criado para Veículo Placa '{placa}'")
        s = Sinistro(id_sinistro, placa, franquia)
        s.adicionar_laudo("Perda Parcial", reparo)
        s.calcular_indenizacao()
        print("[Controller] Sinistro liquidado e enviado para pagamento da oficina!")

if __name__ == "__main__":
    print("=== Esteira de Sinistro Automotivo (GRASP Completo) ===")
    ctrl = SinistroProcessamentoController()
    ctrl.processar_esteira("SIN-8812", "ABC-1234", 2000.00, 8500.00)