Skip to content

Instantly share code, notes, and snippets.

@rg3915
Created November 7, 2024 02:02
Show Gist options
  • Save rg3915/d09b4000e6a8ed7d502444b906cd785a to your computer and use it in GitHub Desktop.
Save rg3915/d09b4000e6a8ed7d502444b906cd785a to your computer and use it in GitHub Desktop.
template method x state machine - Claude

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:

  1. 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
  2. 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
  3. 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:

  1. Estrutura do Template Method:

    • formTemplate: Objeto que contém a estrutura base do padrão
    • submit(): Método template principal que define o fluxo
    • Métodos comuns: validateBasicInfo()
    • Métodos específicos: validateSpecificInfo(), processForm(), showResult()
  2. 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
  3. 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
  4. 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:

  1. Selecione o tipo de formulário (Pessoa Física ou Jurídica)
  2. Preencha os campos básicos (comuns)
  3. Preencha os campos específicos do tipo selecionado
  4. Submeta o formulário
  5. 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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment