Problema
Recorrer una colección de diferentes maneras sin saber cómo está implementada internamente.
Propósito
Permite navegar por cualquier colección (array, lista, árbol) usando la misma interfaz. Separa el “cómo recorrer” del “qué recorrer”.
Concepto clave
Interfaz uniforme: hasNext()
y next()
funcionan igual para arrays, listas, árboles o cualquier estructura. Como el for-each
de Java que funciona con cualquier colección.
Casos de uso comunes
- Recorrido de estructuras de datos complejas
- Paginación de resultados
- Streaming de datos
- Navegación en menús o árboles
- Procesamiento de listas grandes
- Múltiples formas de recorrer la misma estructura
¿Quién es quién en Iterator?
Actor | Lo que realmente es | Ejemplo | Analogía |
---|---|---|---|
Iterator | Interfaz que define hasNext() y next() |
BookIterator - define cómo recorrer |
“Forma de recorrer” (interfaz) |
ConcreteIterator | Sabe CÓMO recorrer una estructura | ForwardIterator , GenreIterator |
Bibliotecario específico (por género, autor) |
Aggregate | Interfaz de colección con createIterator() |
BookCollection - define qué iteradores crear |
“Biblioteca” (interfaz) |
ConcreteAggregate | Colección que crea sus iteradores | Library - crea iteradores para sí misma |
Biblioteca real (tiene libros) |
Diagrama
classDiagram
namespace IteratorPattern {
class Iterator {
<<interface>>
+hasNext() boolean
+next() Object
+remove()
}
class ConcreteIterator {
-collection: ConcreteAggregate
-currentPosition: int
+hasNext() boolean
+next() Object
+remove()
}
class Aggregate {
<<interface>>
+createIterator() Iterator
}
class ConcreteAggregate {
-items: List
+createIterator() Iterator
+getItem(index) Object
+getCount() int
}
}
Iterator <|.. ConcreteIterator
Aggregate <|.. ConcreteAggregate
ConcreteIterator --> ConcreteAggregate
ConcreteAggregate --> ConcreteIterator : creates
Ejemplo práctico
classDiagram
namespace BookCollectionExample {
class BookIterator {
<<interface>>
+hasNext() boolean
+next() Book
}
class BookCollection {
<<interface>>
+createIterator() BookIterator
+createReverseIterator() BookIterator
+createGenreIterator(genre) BookIterator
}
class Library {
-books: List~Book~
+createIterator() BookIterator
+createReverseIterator() BookIterator
+createGenreIterator(genre) BookIterator
+addBook(book)
+removeBook(book)
}
class ForwardIterator {
-library: Library
-position: int
+hasNext() boolean
+next() Book
}
class ReverseIterator {
-library: Library
-position: int
+hasNext() boolean
+next() Book
}
class GenreIterator {
-library: Library
-genre: String
-position: int
+hasNext() boolean
+next() Book
}
class Book {
-title: String
-author: String
-genre: String
}
}
BookIterator <|.. ForwardIterator
BookIterator <|.. ReverseIterator
BookIterator <|.. GenreIterator
BookCollection <|.. Library
ForwardIterator --> Library
ReverseIterator --> Library
GenreIterator --> Library
Library --> Book
Tipos de iteradores
graph TB
A[Iterator Types] --> B[Forward Iterator]
A --> C[Reverse Iterator]
A --> D[Bidirectional Iterator]
A --> E[Random Access Iterator]
A --> F[Filtered Iterator]
B --> B1["Recorre hacia adelante<br/>Más común y simple"]
C --> C1["Recorre hacia atrás<br/>Útil para deshacer operaciones"]
D --> D1["Recorre en ambas direcciones<br/>Navegación flexible"]
E --> E1["Acceso directo por índice<br/>Saltos eficientes"]
F --> F1["Filtra elementos durante recorrido<br/>Criterios específicos"]
Flujo de iteración
sequenceDiagram
participant Client
participant Collection
participant Iterator
Client->>Collection: createIterator()
Collection->>Iterator: new ConcreteIterator(this)
Iterator-->>Collection: iterator instance
Collection-->>Client: iterator
loop While has more elements
Client->>Iterator: hasNext()
Iterator-->>Client: true/false
alt Has next element
Client->>Iterator: next()
Iterator->>Collection: getItem(currentPosition)
Collection-->>Iterator: item
Iterator->>Iterator: increment position
Iterator-->>Client: item
Client->>Client: process(item)
end
end
Ventajas
- Encapsulación: Oculta la representación interna de la colección
- Múltiples recorridos: Diferentes formas de recorrer la misma estructura
- Interfaz uniforme: Misma interfaz para diferentes tipos de colecciones
- Separación de responsabilidades: Recorrido separado de la estructura
Desventajas
- Complejidad: Introduce clases adicionales
- Overhead: Puede ser más lento que acceso directo
- Estado: Mantener estado del iterador puede ser complejo
- Concurrencia: Problemas si la colección cambia durante iteración
Cuándo usar
- Necesitas diferentes formas de recorrer la misma colección
- Quieres ocultar la complejidad interna de la estructura
- Necesitas una interfaz uniforme para diferentes tipos de colecciones
- Quieres poder pausar y reanudar el recorrido
Cuándo NO usar
- Solo necesitas una forma simple de recorrido
- La colección es muy simple (array básico)
- El acceso directo por índice es suficiente
- La complejidad adicional no aporta valor