Vou explicar os dois padrões de design e suas principais diferenças.
O Template Method é um padrão de design comportamental que define o esqueleto de um algoritmo em uma classe base, mas permite que subclasses sobrescrevam etapas específicas sem alterar a estrutura geral. Veja um exemplo:
from abc import ABC, abstractmethod
class Bebida(ABC):
# Este é o template method
def preparar(self):
self.ferver_agua()
self.adicionar_ingredientes()
self.servir()
def ferver_agua(self):
print("Fervendo água...")
@abstractmethod
def adicionar_ingredientes(self):
pass
def servir(self):
print("Servindo na xícara...")
class Cafe(Bebida):
def adicionar_ingredientes(self):
print("Adicionando pó de café...")
class Cha(Bebida):
def adicionar_ingredientes(self):
print("Adicionando saquinho de chá...")
Já a Máquina de Estados (State Machine) é um padrão diferente que permite que um objeto altere seu comportamento quando seu estado interno muda. É como se o objeto parecesse mudar de classe. Por exemplo:
class Estado:
def manipular(self, pedido):
pass
class EmProcessamento(Estado):
def manipular(self, pedido):
pedido.estado = Enviado()
return "Pedido processado e enviado"
class Enviado(Estado):
def manipular(self, pedido):
pedido.estado = Entregue()
return "Pedido entregue"
class Entregue(Estado):
def manipular(self, pedido):
return "Pedido já foi entregue"
class Pedido:
def __init__(self):
self.estado = EmProcessamento()
def proximo_estado(self):
return self.estado.manipular(self)
As principais diferenças são:
-
Propósito:
- Template Method: Define uma estrutura algorítmica fixa, permitindo variações em certas etapas
- Máquina de Estados: Gerencia mudanças de comportamento baseadas em estados
-
Flexibilidade:
- Template Method: A sequência de passos é fixa, só permite personalizar passos específicos
- Máquina de Estados: Mais flexível, permite diferentes transições e comportamentos para cada estado
-
Uso:
- Template Method: Ideal para processos que seguem uma sequência definida mas com variações em alguns passos
- Máquina de Estados: Melhor para objetos que precisam mudar comportamento dinamicamente baseado em estados
Vou criar um exemplo de Template Method usando AlpineJS e PicoCSS para mostrar diferentes tipos de formulários de cadastro que seguem uma mesma estrutura.
<!DOCTYPE html>
<html lang="pt-BR">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Template Method - Formulários de Cadastro</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/picocss/1.5.10/pico.min.css">
<script defer src="https://cdnjs.cloudflare.com/ajax/libs/alpinejs/3.13.3/cdn.min.js"></script>
<style>
.form-section { margin-bottom: 2rem; }
.success { color: green; }
.error { color: red; }
</style>
</head>
<body>
<main class="container" x-data="{
// Template Method Base
formTemplate: {
// Template method principal
submit(formType) {
this.validateBasicInfo();
this.validateSpecificInfo(formType);
this.processForm(formType);
this.showResult(formType);
},
// Passos comuns para todos os formulários
validateBasicInfo() {
if (!this.basicInfo.name || !this.basicInfo.email) {
this.status = 'Nome e email são obrigatórios';
this.isError = true;
return false;
}
return true;
},
// Passos específicos que variam por tipo de formulário
validateSpecificInfo(formType) {
if (formType === 'pessoa') {
if (!this.pessoaInfo.cpf) {
this.status = 'CPF é obrigatório';
this.isError = true;
return false;
}
} else if (formType === 'empresa') {
if (!this.empresaInfo.cnpj || !this.empresaInfo.razaoSocial) {
this.status = 'CNPJ e Razão Social são obrigatórios';
this.isError = true;
return false;
}
}
return true;
},
processForm(formType) {
// Simulando processamento
this.isError = false;
this.status = `Cadastro de ${formType} realizado com sucesso!`;
},
showResult(formType) {
console.log(`Formulário ${formType} processado:`,
formType === 'pessoa' ? this.pessoaInfo : this.empresaInfo
);
}
},
// Estados do formulário
currentForm: 'pessoa',
status: '',
isError: false,
// Dados básicos (comuns a todos os formulários)
basicInfo: {
name: '',
email: ''
},
// Dados específicos de pessoa física
pessoaInfo: {
cpf: ''
},
// Dados específicos de pessoa jurídica
empresaInfo: {
cnpj: '',
razaoSocial: ''
},
// Método para limpar formulário
resetForm() {
this.basicInfo.name = '';
this.basicInfo.email = '';
this.pessoaInfo.cpf = '';
this.empresaInfo.cnpj = '';
this.empresaInfo.razaoSocial = '';
this.status = '';
this.isError = false;
}
}">
<h1>Template Method - Formulários de Cadastro</h1>
<div role="group">
<label>
<input type="radio" x-model="currentForm" value="pessoa" @change="resetForm">
Pessoa Física
</label>
<label>
<input type="radio" x-model="currentForm" value="empresa" @change="resetForm">
Pessoa Jurídica
</label>
</div>
<form @submit.prevent="formTemplate.submit(currentForm)">
<!-- Campos básicos (template) -->
<div class="form-section">
<h2>Informações Básicas</h2>
<label>
Nome
<input type="text" x-model="basicInfo.name" placeholder="Digite seu nome">
</label>
<label>
Email
<input type="email" x-model="basicInfo.email" placeholder="Digite seu email">
</label>
</div>
<!-- Campos específicos para Pessoa Física -->
<div class="form-section" x-show="currentForm === 'pessoa'">
<h2>Informações de Pessoa Física</h2>
<label>
CPF
<input type="text" x-model="pessoaInfo.cpf" placeholder="Digite seu CPF">
</label>
</div>
<!-- Campos específicos para Pessoa Jurídica -->
<div class="form-section" x-show="currentForm === 'empresa'">
<h2>Informações de Pessoa Jurídica</h2>
<label>
CNPJ
<input type="text" x-model="empresaInfo.cnpj" placeholder="Digite seu CNPJ">
</label>
<label>
Razão Social
<input type="text" x-model="empresaInfo.razaoSocial" placeholder="Digite a razão social">
</label>
</div>
<button type="submit">Cadastrar</button>
</form>
<!-- Mensagem de status -->
<p x-show="status"
x-text="status"
:class="{ 'success': !isError, 'error': isError }">
</p>
</main>
</body>
</html>
Este exemplo implementa o padrão Template Method em um contexto web usando AlpineJS. Aqui estão os principais pontos:
-
Estrutura do Template Method:
formTemplate
: Objeto que contém a estrutura base do padrãosubmit()
: Método template principal que define o fluxo- Métodos comuns:
validateBasicInfo()
- Métodos específicos:
validateSpecificInfo()
,processForm()
,showResult()
-
Implementação do Template:
- Campos básicos são comuns a todos os formulários (nome, email)
- Campos específicos variam por tipo (CPF para pessoa física, CNPJ/Razão Social para jurídica)
- A validação e processamento seguem uma estrutura comum mas com regras específicas
-
Características do Template Method:
- Estrutura fixa do algoritmo (validar básico → validar específico → processar → mostrar)
- Flexibilidade nas implementações específicas
- Reutilização de código comum
-
Interface do Usuário:
- Seleção do tipo de formulário (pessoa física/jurídica)
- Campos dinâmicos baseados no tipo selecionado
- Feedback visual de sucesso/erro
- Reset do formulário ao trocar de tipo
Para testar:
- Selecione o tipo de formulário (Pessoa Física ou Jurídica)
- Preencha os campos básicos (comuns)
- Preencha os campos específicos do tipo selecionado
- Submeta o formulário
- Observe as validações e mensagens
Você pode ver como o Template Method mantém uma estrutura consistente enquanto permite variações específicas para cada tipo de formulário.