O Template Method é um padrão comportamental que define o esqueleto de um algoritmo em uma classe base, permitindo que subclasses sobrescrevam etapas específicas do algoritmo sem alterar sua estrutura geral. O padrão encapsula um algoritmo em uma classe, definindo uma sequência de passos, onde alguns desses passos podem ser sobrescritos pelas subclasses.
- Quando você quer permitir que clientes estendam apenas etapas específicas de um algoritmo
- Quando você tem várias classes que contêm algoritmos quase idênticos
- Quando você quer localizar código comum entre várias classes em um único lugar
- Quando você quer controlar em que pontos uma subclasse pode variar um algoritmo
from abc import ABC, abstractmethod
class Relatorio(ABC):
"""Classe abstrata que define o template para geração de relatórios"""
def gerar_relatorio(self) -> None:
"""Template method que define o esqueleto do algoritmo"""
self.cabecalho()
self.corpo()
self.rodape()
self.formatar_saida()
@abstractmethod
def cabecalho(self) -> None:
"""Hook method que deve ser implementado pelas subclasses"""
pass
@abstractmethod
def corpo(self) -> None:
"""Hook method que deve ser implementado pelas subclasses"""
pass
def rodape(self) -> None:
"""Método concreto que pode ser sobrescrito opcionalmente"""
print("\n------- Fim do Relatório -------")
def formatar_saida(self) -> None:
"""Método concreto que pode ser sobrescrito opcionalmente"""
print("\n================================\n")
class RelatorioVendas(Relatorio):
def __init__(self, vendas: list[tuple[str, float]]):
self.vendas = vendas
def cabecalho(self) -> None:
print("RELATÓRIO DE VENDAS")
print("Data: 01/01/2024")
print("================================")
def corpo(self) -> None:
total = 0.0
print("\nVendas realizadas:")
for produto, valor in self.vendas:
print(f"- {produto}: R$ {valor:.2f}")
total += valor
print(f"\nTotal: R$ {total:.2f}")
class RelatorioEstoque(Relatorio):
def __init__(self, estoque: dict[str, int]):
self.estoque = estoque
def cabecalho(self) -> None:
print("RELATÓRIO DE ESTOQUE")
print("Data: 01/01/2024")
print("================================")
def corpo(self) -> None:
print("\nProdutos em estoque:")
for produto, quantidade in self.estoque.items():
status = "OK" if quantidade > 10 else "BAIXO"
print(f"- {produto}: {quantidade} unidades ({status})")
def rodape(self) -> None:
"""Sobrescrevendo o método rodape para adicionar informação adicional"""
print("\nOBS: Produtos com estoque BAIXO precisam ser repostos")
super().rodape()
def main():
# Dados de exemplo
vendas = [
("Notebook", 3500.00),
("Mouse", 50.00),
("Teclado", 150.00)
]
estoque = {
"Notebook": 5,
"Mouse": 15,
"Teclado": 8,
"Monitor": 12
}
# Gerando relatório de vendas
print("\n=== Relatório de Vendas ===")
relatorio_vendas = RelatorioVendas(vendas)
relatorio_vendas.gerar_relatorio()
# Gerando relatório de estoque
print("\n=== Relatório de Estoque ===")
relatorio_estoque = RelatorioEstoque(estoque)
relatorio_estoque.gerar_relatorio()
if __name__ == "__main__":
main()- Permite que clientes sobrescrevam apenas certas partes de um algoritmo grande
- Evita duplicação de código ao mover código comum para uma classe base
- Facilita a manutenção ao centralizar partes do algoritmo
- Permite variações controladas de um mesmo algoritmo
- Aplica o princípio "Hollywood" (Don't call us, we'll call you)
- Alguns clientes podem ficar limitados pelo esqueleto do algoritmo
- Pode violar o princípio de Substituição de Liskov se o template method sobrescrever um método padrão
- Quanto mais passos o template tiver, mais difícil se torna sua manutenção
- Pode levar a uma hierarquia de classes rígida
-
Defina claramente quais passos são:
- Abstratos (devem ser implementados)
- Concretos (implementação padrão)
- Hooks (podem ser sobrescritos)
-
Minimize o número de passos abstratos para reduzir o esforço de implementação das subclasses
-
Considere fornecer implementações padrão para hooks quando possível
-
Use comentários ou documentação para explicar:
- A ordem dos passos
- Quais passos são obrigatórios
- Quais passos podem ser sobrescritos
- O propósito de cada passo
-
Considere usar métodos protegidos para os passos do template para evitar chamadas diretas
-
Pense em usar o padrão Factory Method em conjunto quando precisar criar objetos dentro do template method