Padrões comportamentais: Iterator e Template Method

1. Introdução aos Padrões Comportamentais em Arquitetura de Software

1.1. O papel dos padrões comportamentais na orquestração de fluxos de controle

Padrões comportamentais tratam da comunicação e atribuição de responsabilidades entre objetos. Em Arquitetura de Software, eles definem como os componentes interagem, delegam tarefas e fluem entre si. Diferentemente dos padrões criacionais (foco na instanciação) e estruturais (foco na composição), os comportamentais organizam a lógica de controle, permitindo que sistemas evoluam sem reescrever fluxos inteiros.

1.2. Panorama geral: Iterator (navegação) e Template Method (algoritmo)

Iterator e Template Method são dois padrões complementares. Iterator resolve o problema de percorrer coleções sem expor sua estrutura interna. Template Method define o esqueleto de um algoritmo, permitindo que subclasses implementem partes específicas. Ambos promovem baixo acoplamento e alta coesão, pilares da boa arquitetura.

1.3. Relação com os temas vizinhos

Iterator dialoga com Observer (notificação de mudanças durante iteração) e Visitor (operações sobre elementos percorridos). Template Method relaciona-se com Strategy (variação de algoritmos completos) e Command (encapsulamento de requisições). State, por sua vez, altera comportamento conforme estado interno, complementando Template Method em cenários de máquina de estados.

2. Padrão Iterator: Conceito e Propósito Arquitetural

2.1. Definição do padrão e encapsulamento da iteração

Iterator fornece uma maneira sequencial de acessar elementos de uma coleção sem expor sua representação subjacente. O padrão define uma interface comum para navegação (next(), hasNext(), current()), desacoplando o cliente da estrutura de dados.

2.2. Problema arquitetural resolvido

Sem Iterator, cada cliente precisaria conhecer detalhes da coleção (array, lista, árvore, banco de dados). Isso gera acoplamento forte e dificulta mudanças na estrutura de armazenamento. Iterator resolve isso oferecendo uma abstração de navegação.

2.3. Exemplo de código

interface Iterator<T> {
    boolean hasNext();
    T next();
    void reset();
}

class ListaIterator<T> implements Iterator<T> {
    private List<T> lista;
    private int posicao = 0;

    public ListaIterator(List<T> lista) {
        this.lista = lista;
    }

    public boolean hasNext() {
        return posicao < lista.size();
    }

    public T next() {
        if (!hasNext()) throw new NoSuchElementException();
        return lista.get(posicao++);
    }

    public void reset() {
        posicao = 0;
    }
}

3. Iterator na Arquitetura em Camadas

3.1. Uso na camada de dados

Na camada de dados, Iterator permite percorrer resultados de consultas sem expor conexões ou cursores internos. O cliente apenas consome elementos sequencialmente.

3.2. Aplicação na camada de apresentação

Na apresentação, Iterator viabiliza paginação e listagens infinitas. A interface de usuário solicita o próximo lote sem saber como os dados são armazenados ou recuperados.

3.3. Exemplo de código: iteração de resultados de banco de dados

class DatabaseResultIterator implements Iterator<Record> {
    private ResultSet resultSet;
    private boolean hasNext;
    private boolean moved = false;

    public DatabaseResultIterator(ResultSet rs) {
        this.resultSet = rs;
        this.hasNext = rs.next(); // pré-carrega primeiro
    }

    public boolean hasNext() {
        return hasNext;
    }

    public Record next() {
        if (!hasNext) throw new NoSuchElementException();
        Record current = new Record(resultSet);
        hasNext = resultSet.next();
        return current;
    }

    public void reset() {
        // Não suportado em cursores forward-only
        throw new UnsupportedOperationException();
    }
}

4. Padrão Template Method: Conceito e Propósito Arquitetural

4.1. Definição e esqueleto de algoritmo

Template Method define a estrutura de um algoritmo em uma operação, delegando alguns passos para subclasses. Isso permite reutilizar o fluxo principal enquanto customiza partes específicas.

4.2. Problema arquitetural resolvido

Sem Template Method, cada variação de um processo (ex.: validação, geração de relatório) precisaria reimplementar o fluxo completo, gerando duplicação e inconsistências. O padrão centraliza o fluxo e isola as variações.

4.3. Exemplo de código

abstract class DataProcessor {
    public final void process() {
        loadData();
        validateData();
        transformData();
        saveData();
        notifyComplete();
    }

    protected abstract void loadData();
    protected abstract void validateData();
    protected abstract void transformData();

    protected void saveData() {
        System.out.println("Salvando dados no destino padrão...");
    }

    protected void notifyComplete() {
        System.out.println("Processamento concluído.");
    }
}

class CSVProcessor extends DataProcessor {
    protected void loadData() {
        System.out.println("Carregando CSV...");
    }
    protected void validateData() {
        System.out.println("Validando colunas CSV...");
    }
    protected void transformData() {
        System.out.println("Convertendo CSV para JSON...");
    }
}

5. Template Method na Arquitetura em Camadas

5.1. Aplicação na camada de negócios

Na camada de negócios, Template Method padroniza processos como criação de pedidos, aprovação de transações ou cálculo de impostos. Cada tipo de pedido (físico, digital, assinatura) implementa suas variações.

5.2. Uso em workflows de validação e transformação

Workflows de validação frequentemente seguem etapas fixas: carregar regras, executar verificações, registrar erros. Template Method permite que diferentes domínios (financeiro, RH, logística) customizem as validações.

5.3. Exemplo de código: processamento de pedidos

abstract class OrderProcessor {
    public final void processOrder(Order order) {
        validateOrder(order);
        calculateTotals(order);
        applyDiscounts(order);
        updateInventory(order);
        notifyCustomer(order);
    }

    protected abstract void validateOrder(Order order);
    protected abstract void calculateTotals(Order order);
    protected abstract void applyDiscounts(Order order);

    protected void updateInventory(Order order) {
        System.out.println("Atualizando estoque padrão...");
    }

    protected void notifyCustomer(Order order) {
        System.out.println("Enviando e-mail de confirmação...");
    }
}

class DigitalOrderProcessor extends OrderProcessor {
    protected void validateOrder(Order order) {
        System.out.println("Validando licença digital...");
    }
    protected void calculateTotals(Order order) {
        System.out.println("Calculando impostos digitais...");
    }
    protected void applyDiscounts(Order order) {
        System.out.println("Aplicando desconto por volume...");
    }
}

6. Comparação e Integração entre Iterator e Template Method

6.1. Diferenças fundamentais

Iterator foca em navegação sobre uma coleção. Template Method foca em algoritmo com etapas fixas. Enquanto Iterator percorre dados, Template Method executa lógica.

6.2. Cenários de uso combinados

Combinar ambos é comum: um Template Method itera sobre uma coleção usando Iterator e aplica transformações em cada elemento. Isso é típico em sistemas de relatórios, processamento em lote e pipelines de dados.

6.3. Exemplo de código: sistema de relatórios

abstract class ReportGenerator {
    public final void generateReport(Iterator<Data> dataIterator) {
        openReport();
        while (dataIterator.hasNext()) {
            Data item = dataIterator.next();
            processItem(item);
        }
        closeReport();
    }

    protected abstract void openReport();
    protected abstract void processItem(Data item);
    protected abstract void closeReport();
}

class PDFReportGenerator extends ReportGenerator {
    protected void openReport() {
        System.out.println("Criando documento PDF...");
    }
    protected void processItem(Data item) {
        System.out.println("Adicionando linha ao PDF: " + item);
    }
    protected void closeReport() {
        System.out.println("Finalizando PDF...");
    }
}

7. Implicações Arquiteturais e Boas Práticas

7.1. Impacto na manutenibilidade e extensibilidade

Iterator reduz acoplamento entre cliente e coleção. Template Method elimina duplicação de fluxos. Ambos facilitam a adição de novos comportamentos sem modificar código existente.

7.2. Relação com os princípios SOLID

  • Single Responsibility: Iterator separa navegação da coleção; Template Method separa fluxo das implementações.
  • Open/Closed: Ambos permitem extensão sem modificação (novos iteradores ou subclasses).
  • Liskov Substitution: Subclasses de Template Method devem respeitar o contrato do método template.

7.3. Armadilhas comuns

  • Complexidade excessiva: Usar Iterator para coleções simples (arrays) é desnecessário.
  • Abuso de herança: Template Method pode levar a hierarquias profundas. Prefira composição (Strategy) quando o algoritmo variar completamente.
  • Hooks frágeis: Métodos opcionais (hooks) mal documentados podem gerar comportamentos inesperados.

8. Conclusão e Conexão com os Temas Vizinhos

8.1. Resumo dos ganhos arquiteturais

Iterator e Template Method oferecem encapsulamento de navegação e fluxo algorítmico, respectivamente. Ambos promovem baixo acoplamento, alta reutilização e facilidade de manutenção. Em arquiteturas em camadas, Iterator atua na camada de dados/apresentação, enquanto Template Method organiza a lógica de negócios.

8.2. Ponte para os próximos padrões

State e Visitor complementam Iterator (State altera comportamento durante iteração; Visitor adiciona operações a elementos). Command e Strategy estendem Template Method (Command encapsula requisições; Strategy varia algoritmos completos). Esses padrões formam um conjunto coeso para gerenciar fluxos de controle.

8.3. Recomendações de aplicação prática

  • Use Iterator sempre que precisar percorrer coleções sem expor sua estrutura.
  • Use Template Method para processos com etapas fixas e variações previsíveis.
  • Combine ambos em pipelines de processamento de dados.
  • Evite herança profunda: prefira composição com Strategy quando as variações forem muitas.

Referências