💻 Exemplos: Padrões GRASP Fundamentais
Módulo 10: Paradigmas & Padrões • Bloco 2: SOLID & GRASP • Demonstrações Práticas
🏛️ Atribuição Racional de Responsabilidades na Prática: Demonstrações executáveis dos cinco padrões GRASP fundamentais de Craig Larman. No Exemplo 1, projetamos um Ponto de Venda (PDV) onde Information Expert e Creator atuam harmoniosamente para encapsular estado e criação. No Exemplo 2, implementamos um Use-Case Controller coeso desacoplado da interface de entrada, garantindo Baixo Acoplamento e Alta Coesão.
1. Ponto de Venda (PDV): Information Expert e Creator
Neste exemplo, aplicamos o padrão Creator para que a classe Venda instancie e agregue seus próprios itens (ItemVenda), e o padrão Information Expert para que o cálculo financeiro permaneça onde os dados residem:
# grasp_expert_creator_pdv.py
from dataclasses import dataclass
from typing import List, Dict
# Entidade do Catálogo de Produtos (Imutável)
@dataclass(frozen=True)
class EspecificacaoProduto:
codigo_barras: str
descricao: str
preco_unitario: float
aliquota_imposto: float
# Linha da Venda
class ItemVenda:
def __init__(self, produto: EspecificacaoProduto, quantidade: int):
self.produto = produto
self.quantidade = quantidade
# 1. Information Expert: Conhece o produto e a quantidade da linha
def calcular_subtotal_bruto(self) -> float:
return self.produto.preco_unitario * self.quantidade
def calcular_imposto_item(self) -> float:
return self.calcular_subtotal_bruto() * self.produto.aliquota_imposto
# Agregador da Venda
class Venda:
def __init__(self, id_venda: str):
self.id_venda = id_venda
self._itens: List[ItemVenda] = []
self._desconto_global: float = 0.0
# 2. Creator: A Venda agrega, contém e tem o contexto para criar ItemVenda
def adicionar_item(self, produto: EspecificacaoProduto, quantidade: int) -> ItemVenda:
item = ItemVenda(produto, quantidade)
self._itens.append(item)
return item
def definir_desconto(self, valor_desconto: float):
self._desconto_global = valor_desconto
# 3. Information Expert: Conhece todas as linhas de item para calcular o total
def calcular_total_bruto(self) -> float:
return sum(item.calcular_subtotal_bruto() for item in self._itens)
def calcular_total_impostos(self) -> float:
return sum(item.calcular_imposto_item() for item in self._itens)
def calcular_total_liquidado(self) -> float:
subtotal = self.calcular_total_bruto()
return max(0.0, subtotal - self._desconto_global) + self.calcular_total_impostos()
def gerar_recibo(self) -> str:
linhas = [f"=== RECIBO DE VENDA #{self.id_venda} ==="]
for it in self._itens:
linhas.append(
f" {it.quantidade}x {it.produto.descricao:25} | "
f"Un: R$ {it.produto.preco_unitario:6.2f} | "
f"Sub: R$ {it.calcular_subtotal_bruto():7.2f}"
)
linhas.append("-" * 55)
linhas.append(f" Subtotal Bruto: R$ {self.calcular_total_bruto():9.2f}")
linhas.append(f" Desconto Concedido: -R$ {self._desconto_global:9.2f}")
linhas.append(f" Tributos Incidentes: +R$ {self.calcular_total_impostos():9.2f}")
linhas.append(f" TOTAL A PAGAR: R$ {self.calcular_total_liquidado():9.2f}")
return "\n".join(linhas)
# Execução Prática
if __name__ == "__main__":
catalogo: Dict[str, EspecificacaoProduto] = {
"789001": EspecificacaoProduto("789001", "Café Gourmet 500g", 28.50, 0.07),
"789002": EspecificacaoProduto("789002", "Leite Integral 1L", 4.80, 0.05),
"789003": EspecificacaoProduto("789003", "Chocolate Amargo 70%", 12.00, 0.12),
}
venda = Venda("PDV-2026-901")
# Venda atua como Criador dos seus próprios itens
venda.adicionar_item(catalogo["789001"], 2)
venda.adicionar_item(catalogo["789002"], 4)
venda.adicionar_item(catalogo["789003"], 1)
venda.definir_desconto(5.00)
print(venda.gerar_recibo())
2. Orquestração com Use-Case Controller e Baixo Acoplamento
Neste exemplo, aplicamos o padrão Controller para separar a camada de apresentação da lógica de domínio, preservando a Alta Coesão e o Baixo Acoplamento:
# grasp_controller_cohesion.py
from typing import Optional, Dict
class CatalogoProdutosMock:
def __init__(self):
self._produtos = {
"SKU_10": EspecificacaoProduto("SKU_10", "Monitor Ultrawide 34'", 2800.0, 0.15),
"SKU_20": EspecificacaoProduto("SKU_20", "Teclado Mecânico Ergonômico", 450.0, 0.10)
}
def buscar_por_codigo(self, codigo: str) -> Optional[EspecificacaoProduto]:
return self._produtos.get(codigo)
class RepositorioVendasMock:
def __init__(self):
self._armazenamento = {}
def salvar(self, venda: Venda):
self._armazenamento[venda.id_venda] = venda
print(f" 💾 [Repositório] Venda #{venda.id_venda} persistida com sucesso no banco de dados.")
# Controller de Caso de Uso: Alto Nível, Alta Coesão, Sem Código Visual
class ProcessarVendaController:
"""Orquestrador do caso de uso 'Realizar Venda'. Não contém código de tela nem SQL direto."""
def __init__(self, catalogo: CatalogoProdutosMock, repositorio: RepositorioVendasMock):
self.catalogo = catalogo
self.repositorio = repositorio
self._venda_corrente: Optional[Venda] = None
def iniciar_nova_venda(self, id_venda: str):
self._venda_corrente = Venda(id_venda)
print(f"\n[Controller] Sessão de venda #{id_venda} iniciada.")
def registrar_leitura_item(self, codigo_produto: str, quantidade: int) -> bool:
if self._venda_corrente is None:
raise RuntimeError("Nenhuma venda em andamento. Inicie a venda primeiro.")
produto = self.catalogo.buscar_por_codigo(codigo_produto)
if produto is None:
print(f" ❌ [Controller] Erro: Produto com código '{codigo_produto}' não cadastrado.")
return False
# Delegação: Creator e Expert entram em ação dentro da Venda
self._venda_corrente.adicionar_item(produto, quantidade)
print(f" 🛒 [Controller] Item adicionado: {quantidade}x {produto.descricao}")
return True
def aplicar_cupom(self, valor_desconto: float):
if self._venda_corrente:
self._venda_corrente.definir_desconto(valor_desconto)
print(f" 🏷️ [Controller] Cupom de R$ {valor_desconto:.2f} aplicado à venda.")
def finalizar_venda(self) -> float:
if self._venda_corrente is None:
raise RuntimeError("Nenhuma venda em andamento.")
total = self._venda_corrente.calcular_total_liquidado()
self.repositorio.salvar(self._venda_corrente)
print(f" ✅ [Controller] Venda finalizada com êxito! Total: R$ {total:.2f}")
venda_fechada = self._venda_corrente
self._venda_corrente = None
return total
# Simulação da Camada de Apresentação (Interface UI/Terminal)
if __name__ == "__main__":
print("\n" + "=" * 65)
print("🎮 SIMULAÇÃO DA CAMADA DE APRESENTAÇÃO VIA CONTROLLER GRASP")
print("=" * 65)
controller = ProcessarVendaController(CatalogoProdutosMock(), RepositorioVendasMock())
# 1. Usuário clica no botão "Nova Venda"
controller.iniciar_nova_venda("VND-2026-0042")
# 2. Leitor óptico bipou itens
controller.registrar_leitura_item("SKU_10", 1)
controller.registrar_leitura_item("SKU_20", 2)
controller.registrar_leitura_item("CODIGO_INVALIDO", 1) # Tratamento de erro pelo controller
# 3. Operador aplicou desconto promocional
controller.aplicar_cupom(100.00)
# 4. Pagamento efetuado e encerramento
controller.finalizar_venda()
Saída Esperada dos Scripts:
=== RECIBO DE VENDA #PDV-2026-901 ===
2x Café Gourmet 500g | Un: R$ 28.50 | Sub: R$ 57.00
4x Leite Integral 1L | Un: R$ 4.80 | Sub: R$ 19.20
1x Chocolate Amargo 70% | Un: R$ 12.00 | Sub: R$ 12.00
-------------------------------------------------------
Subtotal Bruto: R$ 88.20
Desconto Concedido: -R$ 5.00
Tributos Incidentes: +R$ 6.39
TOTAL A PAGAR: R$ 89.59
=================================================================
🎮 SIMULAÇÃO DA CAMADA DE APRESENTAÇÃO VIA CONTROLLER GRASP
=================================================================
[Controller] Sessão de venda #VND-2026-0042 iniciada.
🛒 [Controller] Item adicionado: 1x Monitor Ultrawide 34'
🛒 [Controller] Item adicionado: 2x Teclado Mecânico Ergonômico
❌ [Controller] Erro: Produto com código 'CODIGO_INVALIDO' não cadastrado.
🏷️ [Controller] Cupom de R$ 100.00 aplicado à venda.
💾 [Repositório] Venda #VND-2026-0042 persistida com sucesso no banco de dados.
✅ [Controller] Venda finalizada com êxito! Total: R$ 4105.00