GoF, p. 257 também chamado de Cursor
Iterador
Iterator
Percorra uma coleção sem saber como ela é feita por dentro.
Fornecer uma maneira de acessar sequencialmente os elementos de um objeto agregado sem expor sua representação subjacente.
Formas de visitar uma cidade
Você pode andar a esmo, seguir um app de mapas ou contratar um guia. Cada um é uma estratégia de percurso sobre a mesma cidade, e nenhuma exige que você entenda o plano diretor urbano. Trocar de guia não muda a cidade; e dois turistas podem percorrê-la ao mesmo tempo, cada um no seu ponto.
Coleções guardam dados de formas muito diferentes (array, lista ligada, árvore, grafo, páginas de uma API). Colocar todos os algoritmos de percurso dentro da coleção a incha e quebra sua responsabilidade principal — e o cliente que percorre acaba dependendo da estrutura interna.
Extraia o percurso para um objeto iterador separado, com uma interface uniforme (`próximo()`, `temPróximo()`). Cada iterador guarda sua própria posição, o que permite vários percursos simultâneos e independentes sobre a mesma coleção.
- O cliente acessando
colecao.itensInternos[i]diretamente. - Métodos
percorrerAssim()epercorrerAssado()inchando a classe da coleção.
Estrutura
- Iterator
- Interface de percurso: próximo, tem próximo, valor atual.
- ConcreteIterator
- Implementa um algoritmo de percurso e guarda a posição.
- Aggregate
- Interface que declara um método para obter um iterador.
- ConcreteAggregate
- A coleção; devolve o iterador apropriado (Factory Method).
Implementação
class No { constructor(v, esq = null, dir = null) { Object.assign(this, { v, esq, dir }); } }
class Arvore {
constructor(raiz) { this.raiz = raiz; }
// Em JavaScript, iteradores são de primeira classe: geradores + Symbol.iterator.
*emOrdem(no = this.raiz) {
if (!no) return;
yield* this.emOrdem(no.esq);
yield no.v;
yield* this.emOrdem(no.dir);
}
*emLargura() {
const fila = [this.raiz];
while (fila.length) {
const no = fila.shift();
if (!no) continue;
yield no.v;
fila.push(no.esq, no.dir);
}
}
// O percurso padrão da coleção:
[Symbol.iterator]() { return this.emOrdem(); }
}
const arvore = new Arvore(new No(8, new No(3, new No(1), new No(6)), new No(10, null, new No(14))));
console.log([...arvore.emOrdem()]); // [1, 3, 6, 8, 10, 14]
console.log([...arvore.emLargura()]); // [8, 3, 10, 1, 6, 14]
for (const v of arvore) { /* usa o percurso padrão, sem conhecer a estrutura */ }
// ── Iterador PREGUIÇOSO sobre uma API paginada ────────
async function* todosOsUsuarios(fetchPagina) {
let pagina = 1, resultado;
do {
resultado = await fetchPagina(pagina++); // só busca quando o consumidor pede
yield* resultado.itens;
} while (resultado.temMais);
}
// for await (const u of todosOsUsuarios(api)) { … } ← milhões de itens, memória constante - java.util.Iterator e todo o Java Collections Framework
- Symbol.iterator, generators e for..of no JavaScript
- __iter__ do Python; IEnumerable/IEnumerator do C#
Consequências
- Limpa a coleção e o cliente (Responsabilidade Única).
- Novos percursos e novas coleções entram independentemente (Aberto/Fechado).
- Vários percursos simultâneos, cada um com sua posição.
- Permite pausar e retomar o percurso; viabiliza sequências infinitas e preguiçosas.
- Exagero para coleções simples — um `for` direto é mais claro.
- Pode ser menos eficiente que percorrer a estrutura diretamente.
- A estrutura interna da coleção é complexa e você quer escondê-la.
- Você precisa de vários tipos de percurso sobre a mesma coleção.
- Você quer uma interface de percurso uniforme sobre coleções diferentes.
- Os dados são grandes demais para caber na memória (streams, paginação).
- Você só percorre um array simples do começo ao fim.
Relações com outros padrões
- Composite
- Iterators são a forma natural de percorrer árvores Composite.
- Factory Method
- A coleção usa um Factory Method para devolver o iterador certo.
- Memento
- Um memento pode capturar o estado de um iterador para retomá-lo depois.
- Visitor
- Iterator percorre; Visitor decide o que fazer em cada nó.
Verificação
Por que o estado do percurso fica no ITERADOR e não na coleção?
- Para permitir vários percursos simultâneos e independentes sobre a mesma coleção
- Porque coleções não podem ter campos mutáveis
- Para reduzir o consumo de memória da coleção
- Porque a linguagem exige isso
Se a posição morasse na coleção, dois laços aninhados sobre ela interfeririam um no outro. Iteradores independentes resolvem isso.