Artigo 47 — Design Patterns em Python
Prof. Ricardo Matos Módulo 8 · Testes, Qualidade e Boas Práticas · Artigo 47 de 52
Introdução
Design Patterns — padrões de projeto — são soluções reutilizáveis para problemas recorrentes no desenvolvimento de software. Foram catalogados pelo famoso livro do "Gang of Four" em 1994 e continuam relevantes. Em Python, muitos padrões são mais simples de implementar do que em Java ou C++ graças à tipagem dinâmica, funções de primeira classe e recursos como decoradores e metaclasses. Neste artigo cobrimos os padrões mais úteis no dia a dia, com implementações pythônicas.
Categorias de Padrões
Criacionais Estruturais Comportamentais
───────────────── ───────────────── ─────────────────
Singleton Adapter Strategy
Factory Method Decorator Observer
Abstract Factory Facade Command
Builder Composite Template Method
Prototype Proxy Iterator
Chain of Responsibility
State
Padrões Criacionais
Singleton
Garante que uma classe tenha apenas uma instância:
from threading import Lock
from typing import ClassVar
class Singleton:
"""Singleton thread-safe."""
_instancia: ClassVar["Singleton | None"] = None
_lock: ClassVar[Lock] = Lock()
def __new__(cls):
if cls._instancia is None:
with cls._lock:
if cls._instancia is None: # double-checked locking
cls._instancia = super().__new__(cls)
return cls._instancia
# Versão com decorator — mais Pythônica
def singleton(cls):
instancias = {}
def obter_instancia(*args, **kwargs):
if cls not in instancias:
instancias[cls] = cls(*args, **kwargs)
return instancias[cls]
return obter_instancia
@singleton
class ConfiguracaoApp:
def __init__(self):
self.debug = False
self.versao = "1.0.0"
self.database = "postgresql://localhost/escola"
c1 = ConfiguracaoApp()
c2 = ConfiguracaoApp()
print(c1 is c2) # True — mesma instância
Factory Method
Define uma interface para criar objetos, deixando subclasses decidirem qual classe instanciar:
from abc import ABC, abstractmethod
from dataclasses import dataclass
@dataclass
class Relatorio:
titulo: str
conteudo: str
def exibir(self):
print(f"[{self.__class__.__name__}] {self.titulo}")
class RelatorioPDF(Relatorio):
def exportar(self) -> bytes:
return f"PDF: {self.conteudo}".encode()
class RelatorioExcel(Relatorio):
def exportar(self) -> bytes:
return f"XLSX: {self.conteudo}".encode()
class RelatorioHTML(Relatorio):
def exportar(self) -> str:
return f"<html><body>{self.conteudo}</body></html>"
class FabricaRelatorios:
"""Factory simples baseada em dicionário."""
_tipos = {
"pdf": RelatorioPDF,
"excel": RelatorioExcel,
"html": RelatorioHTML,
}
@classmethod
def criar(cls, tipo: str, titulo: str, conteudo: str) -> Relatorio:
classe = cls._tipos.get(tipo.lower())
if not classe:
raise ValueError(f"Tipo desconhecido: {tipo}. "
f"Opções: {list(cls._tipos)}")
return classe(titulo=titulo, conteudo=conteudo)
@classmethod
def registrar(cls, tipo: str, classe):
"""Extensível sem modificar a factory."""
cls._tipos[tipo] = classe
# Uso
rel = FabricaRelatorios.criar("pdf", "Boletim Março", "Conteúdo...")
rel.exibir()
# Registrando novo tipo sem modificar a classe
class RelatorioCSV(Relatorio):
pass
FabricaRelatorios.registrar("csv", RelatorioCSV)
rel_csv = FabricaRelatorios.criar("csv", "Dados", "a,b,c")
Builder
Constrói objetos complexos passo a passo:
from dataclasses import dataclass, field
from typing import Optional
@dataclass
class Consulta:
tabela: str
campos: list[str] = field(default_factory=lambda: ["*"])
condicoes: list[str] = field(default_factory=list)
ordenacao: Optional[str] = None
limite: Optional[int] = None
offset: int = 0
def to_sql(self) -> str:
campos_str = ", ".join(self.campos)
sql = f"SELECT {campos_str} FROM {self.tabela}"
if self.condicoes:
sql += " WHERE " + " AND ".join(self.condicoes)
if self.ordenacao:
sql += f" ORDER BY {self.ordenacao}"
if self.limite:
sql += f" LIMIT {self.limite}"
if self.offset:
sql += f" OFFSET {self.offset}"
return sql
class ConstrutorConsulta:
"""Builder fluente para consultas SQL."""
def __init__(self, tabela: str):
self._consulta = Consulta(tabela=tabela)
def selecionar(self, *campos: str) -> "ConstrutorConsulta":
self._consulta.campos = list(campos)
return self
def onde(self, condicao: str) -> "ConstrutorConsulta":
self._consulta.condicoes.append(condicao)
return self
def ordenar_por(self, campo: str, direcao: str = "ASC") -> "ConstrutorConsulta":
self._consulta.ordenacao = f"{campo} {direcao}"
return self
def limitar(self, n: int) -> "ConstrutorConsulta":
self._consulta.limite = n
return self
def paginar(self, pagina: int, tamanho: int = 10) -> "ConstrutorConsulta":
self._consulta.limite = tamanho
self._consulta.offset = (pagina - 1) * tamanho
return self
def construir(self) -> Consulta:
return self._consulta
# Interface fluente — method chaining
sql = (
ConstrutorConsulta("alunos")
.selecionar("id", "nome", "nota")
.onde("ativo = true")
.onde("nota >= 6.0")
.ordenar_por("nota", "DESC")
.paginar(pagina=2, tamanho=10)
.construir()
.to_sql()
)
print(sql)
# SELECT id, nome, nota FROM alunos WHERE ativo = true
# AND nota >= 6.0 ORDER BY nota DESC LIMIT 10 OFFSET 10
Padrões Estruturais
Adapter
Converte a interface de uma classe em outra que o cliente espera:
from abc import ABC, abstractmethod
class ProcessadorPagamento(ABC):
@abstractmethod
def processar(self, valor: float, descricao: str) -> dict:
...
# API do Stripe — interface que não controlamos
class StripeAPI:
def charge(self, amount_cents: int, currency: str, description: str) -> dict:
return {
"id": "ch_123",
"status": "succeeded",
"amount": amount_cents,
}
# API do PayPal — outra interface
class PayPalAPI:
def create_payment(self, total: float, currency: str, note: str) -> dict:
return {
"paymentId": "PAY-456",
"state": "approved",
"total": total
}
# Adapters — fazem as APIs externas conformarem à nossa interface
class AdapterStripe(ProcessadorPagamento):
def __init__(self, api: StripeAPI):
self._api = api
def processar(self, valor: float, descricao: str) -> dict:
centavos = int(valor * 100)
resultado = self._api.charge(centavos, "BRL", descricao)
return {
"sucesso": resultado["status"] == "succeeded",
"id": resultado["id"],
"valor": resultado["amount"] / 100,
"gateway": "stripe"
}
class AdapterPayPal(ProcessadorPagamento):
def __init__(self, api: PayPalAPI):
self._api = api
def processar(self, valor: float, descricao: str) -> dict:
resultado = self._api.create_payment(valor, "BRL", descricao)
return {
"sucesso": resultado["state"] == "approved",
"id": resultado["paymentId"],
"valor": resultado["total"],
"gateway": "paypal"
}
def cobrar(processador: ProcessadorPagamento, valor: float, desc: str):
"""Funciona com qualquer gateway via Adapter."""
resultado = processador.processar(valor, desc)
status = "✓" if resultado["sucesso"] else "✗"
print(f"{status} [{resultado['gateway']}] R${resultado['valor']:.2f} — {desc}")
cobrar(AdapterStripe(StripeAPI()), 150.00, "Mensalidade Março")
cobrar(AdapterPayPal(PayPalAPI()), 150.00, "Mensalidade Março")
Facade
Fornece interface simplificada para um subsistema complexo:
class SubsistemaEmail:
def conectar(self, host: str): ...
def autenticar(self, user: str, senha: str): ...
def enviar(self, para: str, assunto: str, corpo: str): ...
def desconectar(self): ...
class SubsistemaTemplate:
def carregar(self, nome: str) -> str: ...
def renderizar(self, template: str, contexto: dict) -> str: ...
class SubsistemaFila:
def enfileirar(self, tarefa: dict): ...
def processar(self): ...
class FacadeNotificacao:
"""
Facade — simplifica envio de e-mails com template e fila.
O cliente não precisa conhecer os subsistemas internos.
"""
def __init__(self):
self._email = SubsistemaEmail()
self._template = SubsistemaTemplate()
self._fila = SubsistemaFila()
def enviar_boas_vindas(self, email: str, nome: str):
self._fila.enfileirar({
"tipo": "email",
"template": "boas_vindas",
"para": email,
"contexto": {"nome": nome}
})
def enviar_boletim(self, email: str, aluno: dict):
template = self._template.carregar("boletim")
corpo = self._template.renderizar(template, aluno)
self._email.conectar("smtp.gmail.com")
self._email.autenticar("escola@email.com", "senha")
self._email.enviar(email, "Seu Boletim", corpo)
self._email.desconectar()
# Interface simples para o cliente
notif = FacadeNotificacao()
notif.enviar_boas_vindas("ana@email.com", "Ana")
Padrões Comportamentais
Strategy
Define uma família de algoritmos intercambiáveis:
from abc import ABC, abstractmethod
from typing import Protocol
class EstrategiaOrdenacao(Protocol):
def ordenar(self, alunos: list) -> list: ...
class OrdenarPorNome:
def ordenar(self, alunos: list) -> list:
return sorted(alunos, key=lambda a: a["nome"])
class OrdenarPorNota:
def ordenar(self, alunos: list) -> list:
return sorted(alunos, key=lambda a: a["nota"], reverse=True)
class OrdenarPorFaltas:
def ordenar(self, alunos: list) -> list:
return sorted(alunos, key=lambda a: a["faltas"])
class GerenciadorTurma:
def __init__(self, estrategia: EstrategiaOrdenacao):
self._estrategia = estrategia
def definir_estrategia(self, estrategia: EstrategiaOrdenacao):
self._estrategia = estrategia
def listar(self, alunos: list) -> list:
return self._estrategia.ordenar(alunos)
alunos = [
{"nome": "Carla", "nota": 8.5, "faltas": 2},
{"nome": "Ana", "nota": 9.5, "faltas": 0},
{"nome": "Bruno", "nota": 7.0, "faltas": 5},
]
gerenciador = GerenciadorTurma(OrdenarPorNota())
print("Por nota:", [a["nome"] for a in gerenciador.listar(alunos)])
gerenciador.definir_estrategia(OrdenarPorNome())
print("Por nome:", [a["nome"] for a in gerenciador.listar(alunos)])
# Versão funcional com Python — Strategy como função
def listar_com_criterio(alunos: list, criterio) -> list:
return sorted(alunos, key=criterio)
print("Lambda:", [a["nome"] for a in
listar_com_criterio(alunos, lambda a: -a["nota"])])
Observer
Define dependência um-para-muitos — quando um objeto muda, todos os dependentes são notificados:
from abc import ABC, abstractmethod
from typing import Any
from dataclasses import dataclass
from datetime import datetime
@dataclass
class Evento:
tipo: str
dados: dict
timestamp: datetime = None
def __post_init__(self):
if self.timestamp is None:
self.timestamp = datetime.utcnow()
class Observador(ABC):
@abstractmethod
def atualizar(self, evento: Evento) -> None: ...
class SistemaEventos:
"""Event bus simples — publisher/subscriber."""
def __init__(self):
self._ouvintes: dict[str, list[Observador]] = {}
def inscrever(self, tipo_evento: str, observador: Observador):
self._ouvintes.setdefault(tipo_evento, []).append(observador)
def desinscrever(self, tipo_evento: str, observador: Observador):
if tipo_evento in self._ouvintes:
self._ouvintes[tipo_evento].discard(observador)
def publicar(self, evento: Evento):
for obs in self._ouvintes.get(evento.tipo, []):
try:
obs.atualizar(evento)
except Exception as e:
print(f"Erro no observador {obs}: {e}")
# Ouvintes de todos os eventos
for obs in self._ouvintes.get("*", []):
obs.atualizar(evento)
# Observadores concretos
class NotificadorEmail(Observador):
def atualizar(self, evento: Evento):
if evento.tipo == "aluno.aprovado":
print(f"[Email] Parabéns {evento.dados['nome']}! Você foi aprovado.")
class AuditoriaLogger(Observador):
def atualizar(self, evento: Evento):
print(f"[Auditoria] {evento.timestamp} | {evento.tipo} | {evento.dados}")
class AtualizadorPainel(Observador):
def atualizar(self, evento: Evento):
if evento.tipo in ["aluno.aprovado", "aluno.reprovado"]:
print(f"[Painel] Atualizando dashboard...")
# Uso
bus = SistemaEventos()
bus.inscrever("aluno.aprovado", NotificadorEmail())
bus.inscrever("aluno.aprovado", AuditoriaLogger())
bus.inscrever("aluno.reprovado", AuditoriaLogger())
bus.inscrever("*", AtualizadorPainel())
bus.publicar(Evento(
tipo="aluno.aprovado",
dados={"id": 1, "nome": "Ana Silva", "nota": 9.5}
))
bus.publicar(Evento(
tipo="aluno.reprovado",
dados={"id": 2, "nome": "Bruno Costa", "nota": 4.0}
))
Command
Encapsula uma requisição como objeto — permite desfazer, enfileirar e logar:
from abc import ABC, abstractmethod
from typing import Optional
from collections import deque
class Comando(ABC):
@abstractmethod
def executar(self) -> None: ...
@abstractmethod
def desfazer(self) -> None: ...
class ComandoAdicionarNota(Comando):
def __init__(self, aluno: dict, nota: float):
self.aluno = aluno
self.nota = nota
self._nota_anterior: Optional[float] = None
def executar(self) -> None:
self._nota_anterior = self.aluno.get("nota")
self.aluno["nota"] = self.nota
print(f"Nota {self.nota} adicionada para {self.aluno['nome']}")
def desfazer(self) -> None:
self.aluno["nota"] = self._nota_anterior
print(f"Nota desfeita para {self.aluno['nome']}: {self._nota_anterior}")
class ComandoDesativarAluno(Comando):
def __init__(self, aluno: dict):
self.aluno = aluno
def executar(self) -> None:
self.aluno["ativo"] = False
print(f"Aluno {self.aluno['nome']} desativado")
def desfazer(self) -> None:
self.aluno["ativo"] = True
print(f"Aluno {self.aluno['nome']} reativado")
class GerenciadorComandos:
"""Controla execução e histórico de comandos — suporta undo/redo."""
def __init__(self):
self._historico: deque[Comando] = deque(maxlen=50)
self._desfeitos: deque[Comando] = deque(maxlen=50)
def executar(self, comando: Comando) -> None:
comando.executar()
self._historico.append(comando)
self._desfeitos.clear()
def desfazer(self) -> None:
if not self._historico:
print("Nada para desfazer.")
return
comando = self._historico.pop()
comando.desfazer()
self._desfeitos.append(comando)
def refazer(self) -> None:
if not self._desfeitos:
print("Nada para refazer.")
return
comando = self._desfeitos.pop()
comando.executar()
self._historico.append(comando)
aluno = {"nome": "Ana", "nota": 7.0, "ativo": True}
gerenciador = GerenciadorComandos()
gerenciador.executar(ComandoAdicionarNota(aluno, 9.5))
gerenciador.executar(ComandoDesativarAluno(aluno))
print(f"Estado: {aluno}")
gerenciador.desfazer()
gerenciador.desfazer()
print(f"Após desfazer: {aluno}")
gerenciador.refazer()
print(f"Após refazer: {aluno}")
Chain of Responsibility
Passa uma requisição por uma cadeia de handlers:
from abc import ABC, abstractmethod
from typing import Optional
from dataclasses import dataclass
@dataclass
class Pedido:
valor: float
tipo: str
usuario_id: int
aprovado: bool = False
motivo: str = ""
class Handler(ABC):
def __init__(self):
self._proximo: Optional["Handler"] = None
def definir_proximo(self, handler: "Handler") -> "Handler":
self._proximo = handler
return handler
def processar(self, pedido: Pedido) -> Pedido:
if self._proximo:
return self._proximo.processar(pedido)
return pedido
@abstractmethod
def handle(self, pedido: Pedido) -> Optional[Pedido]: ...
class ValidacaoHandler(Handler):
def handle(self, pedido: Pedido) -> Optional[Pedido]:
if pedido.valor <= 0:
pedido.motivo = "Valor inválido"
pedido.aprovado = False
return pedido
return self.processar(pedido)
class LimiteHandler(Handler):
def __init__(self, limite: float):
super().__init__()
self.limite = limite
def handle(self, pedido: Pedido) -> Optional[Pedido]:
if pedido.valor > self.limite:
pedido.motivo = f"Valor acima do limite R${self.limite:.2f}"
pedido.aprovado = False
return pedido
return self.processar(pedido)
class FraudeHandler(Handler):
def handle(self, pedido: Pedido) -> Optional[Pedido]:
if pedido.usuario_id in [666, 999]:
pedido.motivo = "Usuário bloqueado por suspeita de fraude"
pedido.aprovado = False
return pedido
return self.processar(pedido)
class AprovacaoFinalHandler(Handler):
def handle(self, pedido: Pedido) -> Optional[Pedido]:
pedido.aprovado = True
pedido.motivo = "Aprovado"
return pedido
# Montando a cadeia
validacao = ValidacaoHandler()
limite = LimiteHandler(5000.0)
fraude = FraudeHandler()
aprovacao = AprovacaoFinalHandler()
validacao.definir_proximo(limite).definir_proximo(fraude).definir_proximo(aprovacao)
pedidos = [
Pedido(valor=250.00, tipo="compra", usuario_id=1),
Pedido(valor=-10.00, tipo="compra", usuario_id=2),
Pedido(valor=8000.00, tipo="saque", usuario_id=3),
Pedido(valor=100.00, tipo="compra", usuario_id=666),
]
for p in pedidos:
resultado = validacao.handle(p)
status = "✓" if resultado.aprovado else "✗"
print(f"{status} R${p.valor:>8.2f} | {resultado.motivo}")
Template Method
Define o esqueleto de um algoritmo, deixando subclasses preencherem os detalhes:
from abc import ABC, abstractmethod
class ProcessadorRelatorio(ABC):
"""Template Method — define o fluxo, subclasses definem os passos."""
def gerar(self, dados: list) -> str:
"""Método template — não deve ser sobrescrito."""
dados_limpos = self.limpar_dados(dados)
dados_proc = self.processar(dados_limpos)
conteudo = self.formatar(dados_proc)
cabecalho = self.cabecalho()
rodape = self.rodape()
return f"{cabecalho}\n{conteudo}\n{rodape}"
def limpar_dados(self, dados: list) -> list:
"""Hook — comportamento padrão pode ser sobrescrito."""
return [d for d in dados if d is not None]
@abstractmethod
def processar(self, dados: list) -> list: ...
@abstractmethod
def formatar(self, dados: list) -> str: ...
def cabecalho(self) -> str:
return "=== RELATÓRIO ==="
def rodape(self) -> str:
return f"Total: {0} registros"
class RelatorioAprovados(ProcessadorRelatorio):
def processar(self, dados: list) -> list:
return [d for d in dados if d.get("nota", 0) >= 6.0]
def formatar(self, dados: list) -> str:
linhas = [f" ✓ {d['nome']:20} — {d['nota']:.1f}" for d in dados]
return "\n".join(linhas)
def rodape(self) -> str:
return "Aprovados listados acima."
class RelatorioRanking(ProcessadorRelatorio):
def processar(self, dados: list) -> list:
return sorted(dados, key=lambda d: d.get("nota", 0), reverse=True)
def formatar(self, dados: list) -> str:
linhas = [
f" {i:2}. {d['nome']:20} — {d['nota']:.1f}"
for i, d in enumerate(dados, 1)
]
return "\n".join(linhas)
def cabecalho(self) -> str:
return "=== RANKING DE NOTAS ==="
alunos = [
{"nome": "Ana", "nota": 9.5},
{"nome": "Bruno", "nota": 4.0},
{"nome": "Carla", "nota": 8.5},
{"nome": "Diego", "nota": 6.0},
]
print(RelatorioAprovados().gerar(alunos))
print()
print(RelatorioRanking().gerar(alunos))
Exemplo Completo: Sistema de Processamento de Pagamentos
from abc import ABC, abstractmethod
from dataclasses import dataclass, field
from typing import Optional, Protocol
from datetime import datetime
from collections import deque
import uuid
# ── Domínio ───────────────────────────────────────────
@dataclass
class Pagamento:
id: str = field(default_factory=lambda: str(uuid.uuid4())[:8])
valor: float = 0.0
descricao: str = ""
gateway: str = ""
status: str = "pendente"
timestamp: datetime = field(default_factory=datetime.utcnow)
tentativas: int = 0
erro: Optional[str] = None
# ── Observer: eventos do pagamento ────────────────────
class ObservadorPagamento(Protocol):
def on_pagamento(self, pagamento: Pagamento) -> None: ...
class LogPagamento:
def on_pagamento(self, p: Pagamento):
print(f" [Log] {p.status.upper()} | {p.id} | R${p.valor:.2f}")
class NotificadorPagamento:
def on_pagamento(self, p: Pagamento):
if p.status == "aprovado":
print(f" [Email] Pagamento de R${p.valor:.2f} aprovado!")
elif p.status == "recusado":
print(f" [Email] Pagamento recusado: {p.erro}")
# ── Strategy: gateways ────────────────────────────────
class GatewayPagamento(Protocol):
def cobrar(self, valor: float, descricao: str) -> dict: ...
class GatewaySimulado:
def __init__(self, nome: str, taxa_sucesso: float = 0.9):
self.nome = nome
self.taxa_sucesso = taxa_sucesso
import random
self._rand = random.Random(42)
def cobrar(self, valor: float, descricao: str) -> dict:
sucesso = self._rand.random() < self.taxa_sucesso
return {
"sucesso": sucesso,
"id_externo": f"{self.nome[:3].upper()}-{uuid.uuid4().hex[:6]}",
"erro": None if sucesso else "Cartão recusado"
}
# ── Chain: validações ─────────────────────────────────
class ValidadorPagamento(ABC):
def __init__(self):
self._proximo: Optional["ValidadorPagamento"] = None
def proximo(self, v: "ValidadorPagamento") -> "ValidadorPagamento":
self._proximo = v
return v
def validar(self, p: Pagamento) -> bool:
resultado = self._checar(p)
if resultado and self._proximo:
return self._proximo.validar(p)
return resultado
@abstractmethod
def _checar(self, p: Pagamento) -> bool: ...
class ValidarValor(ValidadorPagamento):
def _checar(self, p: Pagamento) -> bool:
if p.valor <= 0:
p.status = "recusado"
p.erro = "Valor inválido"
return False
return True
class ValidarLimite(ValidadorPagamento):
def __init__(self, limite: float):
super().__init__()
self.limite = limite
def _checar(self, p: Pagamento) -> bool:
if p.valor > self.limite:
p.status = "recusado"
p.erro = f"Acima do limite R${self.limite:.2f}"
return False
return True
# ── Facade: orquestra tudo ────────────────────────────
class ProcessadorPagamentos:
def __init__(self, gateway: GatewayPagamento):
self._gateway = gateway
self._observadores: list = [LogPagamento(), NotificadorPagamento()]
self._historico: deque[Pagamento] = deque(maxlen=100)
# Cadeia de validação
v1 = ValidarValor()
v2 = ValidarLimite(10_000.0)
v1.proximo(v2)
self._validador = v1
def processar(self, valor: float, descricao: str) -> Pagamento:
p = Pagamento(valor=valor, descricao=descricao)
print(f"\nProcessando: R${valor:.2f} — {descricao}")
# Validação (Chain of Responsibility)
if not self._validador.validar(p):
self._notificar(p)
self._historico.append(p)
return p
# Cobrança (Strategy)
p.tentativas += 1
resultado = self._gateway.cobrar(valor, descricao)
if resultado["sucesso"]:
p.status = "aprovado"
p.gateway = resultado["id_externo"]
else:
p.status = "recusado"
p.erro = resultado["erro"]
# Notificação (Observer)
self._notificar(p)
self._historico.append(p)
return p
def _notificar(self, p: Pagamento):
for obs in self._observadores:
obs.on_pagamento(p)
def relatorio(self) -> dict:
aprovados = [p for p in self._historico if p.status == "aprovado"]
recusados = [p for p in self._historico if p.status == "recusado"]
return {
"total": len(self._historico),
"aprovados": len(aprovados),
"recusados": len(recusados),
"valor_total": sum(p.valor for p in aprovados),
}
# ── Executando ────────────────────────────────────────
gateway = GatewaySimulado("Stripe", taxa_sucesso=0.8)
processador = ProcessadorPagamentos(gateway)
pagamentos = [
(250.00, "Mensalidade Março"),
(-10.00, "Valor inválido"),
(15000.00, "Acima do limite"),
(89.90, "Material didático"),
(500.00, "Matrícula"),
]
for valor, desc in pagamentos:
processador.processar(valor, desc)
print("\n=== Relatório ===")
rel = processador.relatorio()
for k, v in rel.items():
print(f" {k:15}: {v}")
Resumo
- Singleton garante instância única — use com moderação, dificulta testes
- Factory Method centraliza criação de objetos — extensível sem modificar código existente
- Builder constrói objetos complexos passo a passo com interface fluente
- Adapter faz interfaces incompatíveis funcionarem juntas — essencial para integrar APIs externas
- Facade simplifica subsistemas complexos com uma interface única e coesa
- Strategy torna algoritmos intercambiáveis — elimina condicionais em código de negócio
- Observer desacopla produtor de consumidores — base de sistemas orientados a eventos
- Command encapsula operações como objetos — habilita undo, redo e histórico
- Chain of Responsibility passa requisições por validadores encadeados — pipelines de processamento
- Template Method define esqueleto de algoritmo — subclasses preenchem os detalhes
Referências e Leituras Complementares
- Refactoring Guru — Padrões de Projeto — https://refactoring.guru/pt-br/design-patterns
- Python Patterns — repositório — https://github.com/faif/python-patterns
- GAMMA, Erich et al. Design Patterns: Elements of Reusable Object-Oriented Software. Addison-Wesley, 1994. — o livro original do Gang of Four.
- PHILLIPS, Dusty; LOTT, Steven F. Python Object-Oriented Programming. 4. ed. Packt, 2021. Cap. 10–12 — design patterns com exemplos modernos em Python.
- PERCIVAL, Harry; GREGORY, Bob. Architecture Patterns with Python. O'Reilly Media, 2020. — Repository, Unit of Work, Event-Driven Architecture em Python.
Prof. Ricardo Matos — Dominando o Python em 1 Ano · Artigo 47 de 52
✅ Módulo 8 Concluído — Testes, Qualidade e Boas Práticas
Plano Completo da Série — Estado Atual
| Módulo | Tema | Artigos | Status |
|---|---|---|---|
| 1 | Fundamentos da Linguagem | 01–06 | ✅ Concluído |
| 2 | Estruturas de Dados e Algoritmos | 07–12 | ✅ Concluído |
| 3 | Orientação a Objetos | 13–18 | ✅ Concluído |
| 4 | Arquivos, I/O e Banco de Dados | 19–24 | ✅ Concluído |
| 5 | Python para Web | 25–30 | ✅ Concluído |
| 6 | Automação e Scripts | 31–34 | ✅ Concluído |
| 7 | Data Science e Machine Learning | 35–42 | ✅ Concluído |
| 8 | Testes, Qualidade e Boas Práticas | 43–47 | ✅ Concluído |
| 9 | Projetos Reais e Carreira | 48–52 | ⬜ Pendente |
Próximo: Módulo 9 · Artigo 48 — Projeto 1: API REST completa com FastAPI, PostgreSQL e Docker
Digite "Sim" para continuar.
you asked
Sim