🧩 Exercícios: Padrões GRASP Avançados
Módulo 10: Paradigmas & Padrões • Bloco 2: SOLID & GRASP • Trilha em 4 Níveis
🎯 Nível 1: Fundamentos
Problema 08.1 — Pure Fabrication: Isolando Persistência de Dados
Contexto: Colocar comandos SQL diretamente dentro da classe de domínio Usuario violaria a Alta Coesão e o Baixo Acoplamento.
Requisitos de Execução:
- Crie a entidade de domínio puro
Usuario(com atributosid,nomeeemail). - Crie a classe de Pure Fabrication
UsuarioRepositoryresponsável exclusivamente por salvar, consultar e simular a persistência em memória.
Resultado Esperado
=== Pure Fabrication: Repositório de Usuários ===
[Domínio Puro] Criado objeto Usuario: Rodrigo (rodrigo@email.com)
[Pure Fabrication] Persistindo usuário #401 no Repositório...
[Consulta] Usuário recuperado com sucesso: Rodrigo <rodrigo@email.com>
📤 Instruções de Entrega (Microsoft Teams)
- Salve o arquivo como:
Atividade_08_1_SeuNome.py - Envie na tarefa:
Atividade Cap 08 - Padrões GRASP Avançados
🔑 Gabarito de Código & Solução Comentada
# Atividade_08_1_Gabarito.py
from typing import Dict, Optional
# 1. Entidade de Domínio Puro
class Usuario:
def __init__(self, id_usuario: int, nome: str, email: str):
self.id = id_usuario
self.nome = nome
self.email = email
# 2. Pure Fabrication: Classe de infraestrutura artificial
class UsuarioRepository:
def __init__(self):
self._tabela_db: Dict[int, Usuario] = {}
def salvar(self, usuario: Usuario) -> None:
self._tabela_db[usuario.id] = usuario
print(f"[Pure Fabrication] Persistindo usuário #{usuario.id} no Repositório...")
def buscar_por_id(self, id_usuario: int) -> Optional[Usuario]:
return self._tabela_db.get(id_usuario)
if __name__ == "__main__":
print("=== Pure Fabrication: Repositório de Usuários ===")
user = Usuario(401, "Rodrigo", "rodrigo@email.com")
print(f"[Domínio Puro] Criado objeto Usuario: {user.nome} ({user.email})")
repo = UsuarioRepository()
repo.salvar(user)
res = repo.buscar_por_id(401)
if res:
print(f"[Consulta] Usuário recuperado com sucesso: {res.nome} <{res.email}>")
🔍 Nível 2: Prática
Problema 08.2 — Polymorphism GRASP: Eliminando Estruturas Condicionais de Tipo
Contexto: O padrão Polymorphism do GRASP orienta substituir verificações condicionais repetitivas (if tipo == 'FISICA') por despacho polimórfico dinâmico.
Requisitos de Execução:
- Crie a classe abstrata
Pessoacom métodocalcular_tributo_anual(rendimento: float) -> float. - Implemente
PessoaFisica(alíquota progressiva de $27.5\%$) ePessoaJuridica(alíquota do Simples Nacional de $6\%$). - O
ProcessadorContabildeve calcular impostos sobre uma lista heterogênea sem nenhumisinstanceoutype().
Resultado Esperado
=== Processador Contábil Polimórfico (GRASP) ===
[PessoaFísica: Mariana] Rendimento: R$ 100000.00 -> Tributo Devido: R$ 27500.00
[PessoaJurídica: TechCorp] Faturamento: R$ 500000.00 -> Tributo Devido: R$ 30000.00
Total Geral Arrecadado: R$ 57500.00
📤 Instruções de Entrega (Microsoft Teams)
- Salve o arquivo como:
Atividade_08_2_SeuNome.py - Envie na tarefa:
Atividade Cap 08 - Padrões GRASP Avançados
🔑 Gabarito de Código & Solução Comentada
# Atividade_08_2_Gabarito.py
from abc import ABC, abstractmethod
from typing import List, Tuple
class Pessoa(ABC):
def __init__(self, nome: str):
self.nome = nome
@abstractmethod
def calcular_tributo(self, rendimento: float) -> float: pass
class PessoaFisica(Pessoa):
def calcular_tributo(self, rendimento: float) -> float:
return rendimento * 0.275
class PessoaJuridica(Pessoa):
def calcular_tributo(self, rendimento: float) -> float:
return rendimento * 0.06
if __name__ == "__main__":
contribuintes: List[Tuple[Pessoa, float]] = [
(PessoaFisica("Mariana"), 100000.00),
(PessoaJuridica("TechCorp"), 500000.00)
]
print("=== Processador Contábil Polimórfico (GRASP) ===")
total = 0.0
for p, valor in contribuintes:
tributo = p.calcular_tributo(valor)
total += tributo
tipo = type(p).__name__
print(f"[{tipo}: {p.nome}] Base: R$ {valor:.2f} -> Tributo Devido: R$ {tributo:.2f}")
print(f"Total Geral Arrecadado: R$ {total:.2f}")
🚀 Nível 3: Integração
Problema 08.3 — Indirection & Protected Variations em Gateway de SMS
Contexto: Operadoras de SMS mudam com frequência. O padrão Indirection cria um intermediário que desacopla o sistema da API concreta de terceiros, enquanto Protected Variations protege os clientes contra variações de contrato.
Requisitos de Execução:
- Crie a interface estável
ISMSGateway(Protected Variations). - Implemente o intermediário
SMSDispatcher(Indirection) que gerencia failover automático entreTwilioServiceAdaptereZenviaServiceAdapter. - Caso a operadora primária falhe, o despachante chaveia transparentemente para a secundária.
Resultado Esperado
=== Despacho de Mensageria com Indireção & Failover ===
Tentando envio via Operadora Primária (Twilio)...
[Twilio Adapter] ERRO: 503 Service Unavailable!
[Indirection] Ativando failover para Zenvia...
[Zenvia Adapter] SMS 'Código: 8819' entregue com sucesso!
📤 Instruções de Entrega (Microsoft Teams)
- Salve o arquivo como:
Atividade_08_3_SeuNome.py - Envie na tarefa:
Atividade Cap 08 - Padrões GRASP Avançados
🔑 Gabarito de Código & Solução Comentada
# Atividade_08_3_Gabarito.py
from abc import ABC, abstractmethod
# Protected Variations: Interface estável
class ISMSGateway(ABC):
@abstractmethod
def enviar_sms(self, numero: str, mensagem: str) -> bool: pass
class TwilioServiceAdapter(ISMSGateway):
def enviar_sms(self, numero: str, mensagem: str) -> bool:
print("[Twilio Adapter] ERRO: 503 Service Unavailable!")
return False
class ZenviaServiceAdapter(ISMSGateway):
def enviar_sms(self, numero: str, mensagem: str) -> bool:
print(f"[Zenvia Adapter] SMS '{mensagem}' entregue com sucesso!")
return True
# Indirection: Intermediário que encapsula a lógica de failover
class SMSDispatcher:
def __init__(self, primario: ISMSGateway, secundario: ISMSGateway):
self._primario = primario
self._secundario = secundario
def disparar(self, numero: str, mensagem: str):
print("Tentando envio via Operadora Primária (Twilio)...")
sucesso = self._primario.enviar_sms(numero, mensagem)
if not sucesso:
print("[Indirection] Ativando failover para Zenvia...")
self._secundario.enviar_sms(numero, mensagem)
if __name__ == "__main__":
print("=== Despacho de Mensageria com Indireção & Failover ===")
dispatcher = SMSDispatcher(TwilioServiceAdapter(), ZenviaServiceAdapter())
dispatcher.disparar("+5511999998888", "Código: 8819")
🏆 Nível 4: Desafio
Problema 08.4 — Suíte Completa dos 9 Padrões GRASP em um Motor de Empréstimos Bancários
Contexto: Combine os 9 padrões GRASP (Expert, Creator, Controller, Low Coupling, High Cohesion, Polymorphism, Pure Fabrication, Indirection, Protected Variations) em um motor corporativo de concessão de crédito.
Requisitos de Execução:
- Controller:
EmprestimoControllerrecebe o pedido. - Creator / Expert:
PropostaCreditoinstancia suasParcelae calcula o Custo Efetivo Total (CET). - Polymorphism: Subtipos de
PoliticaRisco(Pessoa Física vs Produtor Rural). - Pure Fabrication:
AuditoriaServicegrava eventos de conformidade. - Indirection & Protected Variations:
MotorBureauCreditoprotege o sistema contra mudanças na API da Serasa.
Resultado Esperado
=== Motor Corporativo GRASP de Crédito ===
[Controller] Recebida proposta de R$ 50000.00 em 12 parcelas
[Indirection Bureau] Consulta Score Serasa: 850 (Risco Baixo)
[Polymorphism] Política PF aplicada: Taxa de 1.49% a.m.
[Creator/Expert] 12 parcelas de R$ 4583.12 geradas. CET Total: R$ 54997.44
[Pure Fabrication] Auditoria registrada: Proposta aprovada em conformidade Bacen.
📤 Instruções de Entrega (Microsoft Teams)
- Salve o arquivo como:
Atividade_08_4_SeuNome.py - Envie na tarefa:
Atividade Cap 08 - Padrões GRASP Avançados
🔑 Gabarito de Código & Solução Comentada
# Atividade_08_4_Gabarito.py
from abc import ABC, abstractmethod
from typing import List
# Protected Variations & Indirection
class IBureauCredito(ABC):
@abstractmethod
def consultar_score(self, doc: str) -> int: pass
class MockBureauSerasa(IBureauCredito):
def consultar_score(self, doc: str) -> int:
print("[Indirection Bureau] Consulta Score Serasa: 850 (Risco Baixo)")
return 850
# Polymorphism
class IPoliticaRisco(ABC):
@abstractmethod
def calcular_taxa(self, score: int) -> float: pass
class PoliticaRiscoPF(IPoliticaRisco):
def calcular_taxa(self, score: int) -> float:
print("[Polymorphism] Política PF aplicada: Taxa de 1.49% a.m.")
return 0.0149
# Pure Fabrication
class AuditoriaService:
def registrar(self, msg: str):
print(f"[Pure Fabrication] Auditoria registrada: {msg}")
# Creator & Information Expert
class Parcela:
def __init__(self, numero: int, valor: float):
self.numero = numero
self.valor = valor
class PropostaCredito:
def __init__(self, valor_total: float, qtd_parcelas: int, taxa: float):
self.valor_total = valor_total
self.qtd_parcelas = qtd_parcelas
self.taxa = taxa
self.parcelas: List[Parcela] = []
def gerar_parcelas(self):
valor_parcela = (self.valor_total * (1 + self.taxa * self.qtd_parcelas)) / self.qtd_parcelas
for i in range(1, self.qtd_parcelas + 1):
self.parcelas.append(Parcela(i, valor_parcela))
cet_total = valor_parcela * self.qtd_parcelas
print(f"[Creator/Expert] {self.qtd_parcelas} parcelas de R$ {valor_parcela:.2f} geradas. CET Total: R$ {cet_total:.2f}")
# Controller
class EmprestimoController:
def __init__(self, bureau: IBureauCredito, politica: IPoliticaRisco, audit: AuditoriaService):
self._bureau = bureau
self._politica = politica
self._audit = audit
def submeter_proposta(self, doc: str, valor: float, parcelas: int):
print(f"[Controller] Recebida proposta de R$ {valor:.2f} em {parcelas} parcelas")
score = self._bureau.consultar_score(doc)
taxa = self._politica.calcular_taxa(score)
prop = PropostaCredito(valor, parcelas, taxa)
prop.gerar_parcelas()
self._audit.registrar("Proposta aprovada em conformidade Bacen.")
if __name__ == "__main__":
print("=== Motor Corporativo GRASP de Crédito ===")
ctrl = EmprestimoController(MockBureauSerasa(), PoliticaRiscoPF(), AuditoriaService())
ctrl.submeter_proposta("123.456.789-00", 50000.0, 12)