Volver al Inicio

Patrones de Diseño de Software

Patrón de Diseño: Iterator

📺 Video Explicativo del Grupo (3-5 min): Ver video en YouTube / OneDrive

"Permite acceder secuencialmente a los elementos de un objeto agregado sin exponer su representación subyacente."


1. El Problema (¿Qué duele antes del patrón?)

Imagina que estás construyendo el backend de una plataforma de e-commerce en El Salvador. Para optimizar las búsquedas rápidas, almacenas el listado de productos internamente en una estructura de Árbol Binario compleja. Al mismo tiempo, el módulo de promociones guarda sus datos en una matriz asociativa simple (un Array plano).

¿Dónde está el dolor real? Si el componente de facturación o la vista del carrito necesitan recorrer ambas colecciones para calcular un gran total, obligas al código cliente a llenarse de bucles foreach tradicionales para el array, combinados con lógicas recursivas pesadas para poder caminar a través del árbol.

El cliente termina conociendo íntimamente cómo están guardados tus datos. Si el día de mañana decides cambiar el árbol binario por una lista enlazada para mejorar el uso de memoria, **romperás inmediatamente todo el código cliente** del sistema que dependía de esa estructura.

2. La Solución (Cómo lo resuelve y Diagrama)

El patrón Iterator extrae la responsabilidad del recorrido de la colección y se la delega a un objeto independiente llamado Iterador. Este objeto encapsula de forma absoluta el estado de la iteración (en qué posición va, cuántos elementos quedan) y expone una interfaz pública unificada.

Gracias a esto, al cliente ya no le importa si los productos provienen de un árbol, un array o una base de datos; simplemente interactúa con el iterador pidiéndole el "siguiente" elemento de manera uniforme.

Diagrama de Clases (Estructura y Relaciones)

+------------------+                   +--------------------+
|    Aggregate     |------------------>|      Iterator      |
+------------------+                   +--------------------+
| + createIterator()|                  | + current()        |
+------------------+                   | + next()           |
         ^                             | + hasNext()        |
         |                             +--------------------+
+------------------+                             ^
|ConcreteAggregate |                             |
+------------------+                   +--------------------+
| - items: array   |                   |  ConcreteIterator  |
| + createIterator()|----------------->|--------------------|
+------------------+                   | - position: int    |
                                       +--------------------+
    

Estructura de Componentes

3. Código Mínimo (Estructura Esencial en PHP)

Esqueleto funcional de unas 25 líneas de lógica que demuestra cómo desacoplar por completo el recorrido del cliente:

<?php
// 1. Interfaz común del Iterador
interface MyIterator {
    public function current();
    public function next(): void;
    public function hasNext(): bool;
}

// 2. Iterador Concreto para Arrays
class ArrayIterator implements MyIterator {
    private int $position = 0;
    public function __construct(private array $items) {}

    public function current() { return $this->items[$this->position]; }
    public function next(): void { $this->position++; }
    public function hasNext(): bool { return isset($this->items[$this->position]); }
}

// 3. Agregado Concreto (La Colección)
class ProductCollection {
    private array $products = [];
    public function addProduct(string $product): void { $this->products[] = $product; }
    public function getIterator(): MyIterator { return new ArrayIterator($this->products); }
}

// 4. Código Cliente: Totalmente agnóstico de la estructura interna
$collection = new ProductCollection();
$collection->addProduct("Laptop");
$collection->addProduct("Mouse");

$iterator = $collection->getIterator();
while ($iterator->hasNext()) {
    echo "Producto: " . $iterator->current() . "\n";
    $iterator->next();
}
    

4. Cuándo SÍ / Cuándo NO (Criterio de Aplicación)

Señales de cuando SÍ aplica:

Señales de cuando NO aplica (Sobre-ingeniería):

5. Un Ejemplo Real en la Industria (Verificable)

Este patrón es tan poderoso que viene construido de manera nativa en el núcleo de los lenguajes modernos mediante interfaces como Iterator o IteratorAggregate de PHP.

Un ejemplo real, robusto y de alto impacto en producción ocurre en las Lazy Collections de Laravel (implementadas bajo el componente Illuminate\Support\LazyCollection).

Cuando ejecutas consultas pesadas a través del ORM Eloquent, intentar cargar un millón de registros a la vez en un array colapsaría la memoria del servidor. Laravel lo soluciona implementando el patrón Iterator apoyado por generadores (utilizando la palabra clave yield).

Esto permite que utilices un bucle estándar en tu controlador, pero manteniendo bajo el capó **un único registro en memoria a la vez** mientras se recorren millones de filas de la base de datos de manera fluida y segura:

use App\Models\User;

// Laravel genera un iterador transparente apoyado en cursores de la base de datos
$users = User::cursor(); 

// El código cliente itera de forma secuencial y ultra limpia
foreach ($users as $user) {
    echo $user->email; // Mantiene el consumo de RAM plano en el servidor
}