🧩 Exercícios: Padrões GRASP Fundamentais
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:
- Crie a classe
ItemCarrinhocomproduto,quantidadeepreco_unitarioe o métodocalcular_subtotal(). - Crie a classe
CarrinhoDeComprasque 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)
- Salve o arquivo como:
Atividade_07_1_SeuNome.py - 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:
- A classe
OrdemDeServicodeve ser a única responsável por criar instâncias deApontamentoHoras. - Implemente o método
ordem.registrar_apontamento(desenvolvedor: str, horas: float, valor_hora: float). - Garanta que o cliente da API não instancie
ApontamentoHorasdiretamente comnew/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)
- Salve o arquivo como:
Atividade_07_2_SeuNome.py - 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:
- Crie o
MatriculaCursoController. - O controlador deve receber requisições de matrícula, validar pré-requisitos no
AlunoDomainService, registrar noTurmaRepositorye disparar o recibo viaFinanceiroService. - 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)
- Salve o arquivo como:
Atividade_07_3_SeuNome.py - 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:
- Aplique Information Expert: O
ContratoSegurocalcula o valor da indenização com base na tabela FIPE e franquia contratada. - Aplique Creator: O
Sinistroinstancia e registra osLaudoPericial. - Aplique Controller: O
SinistroProcessamentoControllerorquestra 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)
- Salve o arquivo como:
Atividade_07_4_SeuNome.py - 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)