Patrón de iterador

format_list_bulleted Contenido keyboard_arrow_down
ImprimirCitar

En la programación orientada a objetos, el patrón de iterador es un patrón de diseño en el que se utiliza un iterador para atravesar un contenedor y acceder a los elementos del contenedor. El patrón iterador desacopla los algoritmos de los contenedores; en algunos casos, los algoritmos son necesariamente específicos del contenedor y, por lo tanto, no se pueden desacoplar.

Por ejemplo, el algoritmo hipotético SearchForElement generalmente se puede implementar utilizando un tipo específico de iterador en lugar de implementarlo como un algoritmo específico del contenedor. Esto permite usar SearchForElement en cualquier contenedor que admita el tipo de iterador requerido.

Resumen

El iterador patrón de diseño es uno de los veintitrés bien conocidos Patrones de diseño de GoF que describen cómo resolver problemas de diseño recurrentes para diseñar software orientado a objetos flexible y reutilizable, es decir, objetos que son más fáciles de implementar, cambiar, probar y reutilizar.

¿Qué problemas puede resolver el patrón de diseño Iterator?

  • Los elementos de un objeto agregado deben ser accedidos y atravesados sin exponer su representación (estructuras de datos).
  • Las nuevas operaciones transversales deben definirse para un objeto agregado sin cambiar su interfaz.

La definición de operaciones de acceso y cruce en la interfaz agregada es inflexible porque compromete el agregado a determinadas operaciones de acceso y cruce y hace que sea imposible agregar nuevas operaciones. más tarde sin tener que cambiar la interfaz agregada.

¿Qué solución describe el patrón de diseño Iterator?

  • Define un objeto separado (iterator) que encapsula el acceso y la exploración de un objeto agregado.
  • Los clientes utilizan un iterador para acceder y atravesar un agregado sin conocer su representación (estructuras de datos).

Se pueden usar diferentes iteradores para acceder y atravesar un agregado de diferentes maneras.
Las nuevas operaciones de acceso y recorrido se pueden definir de forma independiente mediante la definición de nuevos iteradores.

Vea también la clase UML y el diagrama de secuencia a continuación.

Definición

La esencia del patrón Iterator es "Proporcionar una forma de acceder a los elementos de un objeto agregado secuencialmente sin exponer su representación subyacente.".

Estructura

Clase UML y diagrama de secuencia

Un diagrama de clase y secuencia UML muestra para el patrón de diseño Iterator.

En el diagrama de clases UML anterior, la clase Client se refiere (1) a la interfaz Agregate para crear un objeto Iterator (createIterator()) y (2) a la interfaz Iterator para atravesar un objeto Agregate (next(),hasNext()). La clase Iterator1 implementa la interfaz Iterator accediendo a la clase Aggregate1.

El diagrama de secuencia UML muestra las interacciones en tiempo de ejecución: el objeto Client llama a createIterator() en un objeto Aggregate1, que crea un Iterator1 objeto y lo devuelve al Cliente. El Client usa entonces Iterator1 para recorrer los elementos del objeto Agregate1.

Diagrama de clases UML

El patrón de iterador

Implementación específica del idioma

Algunos lenguajes estandarizan la sintaxis. C++ y Python son ejemplos notables.

C#

.NET Framework tiene interfaces especiales que admiten una iteración simple: System.Collections.IEnumerator sobre una colección no genérica y System.Collections.Generic.IEnumerator<T> sobre una colección genérica.

La instrucción C# foreach está diseñada para iterar fácilmente a través de la colección que implementa System.Collections.IEnumerator y/o System.Collections.Generic.IEnumerator<T> interfaz. Desde C# v2, foreach también puede iterar a través de tipos que implementan System.Collections.Generic.IEnumerable<T> y System.Collections.Generic.IEnumerator< T>

Ejemplo de uso de la instrucción foreach:

Var primos = nuevo Lista.int■ {} 2, 3, 5, 7, 11, 13, 17, 19 };largo m = 1;en adelante ()Var primo dentro primos) m *= primo;

C++

C++ implementa iteradores con la semántica de punteros en ese lenguaje. En C++, una clase puede sobrecargar todas las operaciones de puntero, por lo que se puede implementar un iterador que actúe más o menos como un puntero, completo con desreferencia, incremento y disminución. Esto tiene la ventaja de que los algoritmos de C++ como std::sort se pueden aplicar inmediatamente a los búferes de memoria simples y antiguos, y que no hay nueva sintaxis que aprender. Sin embargo, requiere un "fin" iterador para probar la igualdad, en lugar de permitir que un iterador sepa que ha llegado al final. En lenguaje C++, decimos que un iterador modela el concepto de iterador.

Esta implementación de C++11 se basa en el capítulo "Generalizar vector una vez más".

#include ■iostream#include - No.clase Vectorial {}público: utilizando iterator = doble*; iterator comenzar() {} retorno "Elem[0]; } iterator final() {} retorno "Elem[sz]; }  Vectorial()int s) :Elem()nullptr), sz()0) {} si ()s . 0) tiro std::fuera de fuera()"Vector::Vector"); Elem = nuevo doble[s]; sz = s; } ~Vectorial() {} Borrador[] Elem; } int tamaño() const {} retorno sz; } doble #()int n) const {}  si ()n . 0 Silencio n >= sz) tiro std::fuera de fuera()"Vector::get"); retorno Elem[n];  } vacío set()int n, doble v) {}  si ()n . 0 Silencio n >= sz) tiro std::fuera de fuera()"Vector::set"); Elem[n] = v;  } Vectorial()const Vectorial") = Borrador; // regla de tres Vectorial" operador=()const Vectorial") = Borrador;privado: doble* Elem; int sz;};int principal() {} Vectorial v()2); // v tiene 2 elementos v.set()0, 1.1*1.1); v.set()1, 2.2*2.2);  para ()const auto" x : v) {} std::Cout .. x .. 'n '; } para ()auto i = v.comenzar(); i ! v.final(); ++i) {} std::Cout .. *i .. 'n '; } para ()auto i = 0; i . 3; ++i) {} std::Cout .. v.#()i) .. 'n '; } }

La salida del programa es

1.214.841.214.841.214.84terminada llamado después lanzamiento an ejemplo de 'std::fuera de fuera' ¿Qué?(): Vectorial::#

Java

Java tiene la interfaz Iterator.

Un ejemplo simple que muestra cómo devolver números enteros entre [inicio, fin] usando un Iterator

importación Java.util. Iterator;importación java.util.NoSuchElementException;público clase RangeIterator Ejemplo {} público estática Iterator.Integer rango()int Empieza, int final) {} retorno nuevo Iterator() {} privado int índice = Empieza; @Override público boolean haSiguiente() {} retorno índice . final; } @Override público Integer siguiente() {} si ()!haSiguiente()) {} tiro nuevo NoSuchElementException(); } retorno índice++; } }; } público estática vacío principal()String[] args) {} Var iterator = rango()0, 10); mientras ()iterator.haSiguiente()) {} Sistema.Fuera..println()iterator.siguiente()); } // o utilizando un lambda iterator.paraCada()Sistema.Fuera.::println); }}

A partir de Java 5, los objetos que implementan la interfaz Iterable, que devuelve un Iterator desde su único método, se pueden atravesar utilizando la sintaxis de bucle foreach de Java. La interfaz Collection del marco de colecciones de Java amplía Iterable.

Ejemplo de clase Family implementando la interfaz Iterable:

importación Java.util. Iterator;importación Java.util. Set;clase Familia.E implementos Iterable.E {} privado final Set.E elementos; público Familia()Set.E elementos) {} esto.elementos = Set.Copia()elementos); } @Override público Iterator.E iterator() {} retorno elementos.iterator(); }}

La clase IterableExample demuestra el uso de la clase Family:

público clase IterableExample {} público estática vacío principal()String[] args) {} Var weasleys = Set.de() "Arthur", "Molly", "Bill", "Charlie", "Percy", "Fred", "George", "Ron", "Ginny" ); Var familia = nuevo Familia()weasleys); para ()Var Nombre : familia) {} Sistema.Fuera..println()Nombre + "Weasley"); } }}

Salida:

Ron WeasleyMolly WeasleyPercy WeasleyFred WeasleyCharlie WeasleyGeorge WeasleyArthur WeasleyGinny WeasleyBill Weasley

Javascript

JavaScript, como parte de ECMAScript 6, admite el patrón iterador con cualquier objeto que proporcione un método next(), que devuelve un objeto con dos propiedades específicas: done y valor. Aquí hay un ejemplo que muestra un iterador de matriz inversa:

función reversoArrayIterator()array) {} Var índice = array.longitud - 1; retorno {} siguiente: () = índice >= 0 ? {}valor: array[índice--] hecho: falso} : {}hecho: verdadero} }}const es = reversoArrayIterator[Tres ', 'dos ', 'uno ']);consola.log()es.siguiente().valor); //- 'uno'consola.log()es.siguiente().valor); //- 'dos'consola.log()es.siguiente().valor); //- Tres 'consola.log()` ¿Terminaste? ${es.siguiente().hecho}`); //- verdadero

La mayoría de las veces, sin embargo, es deseable proporcionar semántica de iterador en los objetos para que puedan iterarse automáticamente a través de bucles for...of. Algunos de los tipos integrados de JavaScript como Array, Map o Set ya definen su propio comportamiento de iteración. Se puede lograr el mismo efecto definiendo el método meta @@iterator de un objeto, también denominado Symbol.iterator. Esto crea un objeto iterable.

Este es un ejemplo de una función de rango que genera una lista de valores desde start hasta end, exclusivo, usando un for bucle para generar los números:

función rango()Empieza, final) {} retorno {} [Signatura.iterator]() {} // retorno esto; } siguiente() {} si ()Empieza . final) {} retorno {} valor: Empieza++, hecho: falso }; // } retorno {} hecho: verdadero, valor: final }; // } }}para ()Número de rango()1, 5) {} consola.log()Número); // - título 1, 2, 3, 4}

El mecanismo de iteración de los tipos integrados, como las cadenas, también se puede manipular:

Deja iter = ['I', 't ', 'e ', 'r ', 'a', 't ', 'o ', 'r '[ ]Signatura.iterator]();iter.siguiente().valor; //- Iiter.siguiente().valor; //- t

PHP

PHP admite el patrón de iterador a través de la interfaz Iterator, como parte de la distribución estándar. Los objetos que implementan la interfaz se pueden iterar con la construcción de lenguaje foreach.

Ejemplo de patrones usando PHP:

Identificado?php// BookIterator.phpnamespace DesignPatterns;clase BookIterator implementos Iterator{} privado int $i_posición = 0; privado BookCollection $books Colección; público función __construir()BookCollection $books Colección) {} Esto-libros Colección = $books Colección; } público función corriente() {} retorno Esto-libros Colección-# Título()Esto-i_posición); } público función clave(): int {} retorno Esto-i_posición; } público función siguiente(): vacío {} Esto-i_posición++; } público función rebobinado(): vacío {} Esto-i_posición = 0; } público función válido(): bool {} retorno !is_null()Esto-libros Colección-# Título()Esto-i_posición)); }}
Identificado?php// BookCollection.phpnamespace DesignPatterns;clase BookCollection implementos  IteratorAggregate{} privado array $a_titles = array(); público función getIterator() {} retorno nuevo BookIterator()Esto); } público función addTitle()cuerda $string): vacío {} Esto-a_titles[] = $string; } público función # Título()$key) {} si ()isset()Esto-a_titles[$key]) {} retorno Esto-a_titles[$key]; } retorno nulo; } público función is_empty(): bool {} retorno vacío()Esto-$a_titles); }}
Identificado?php// index.phpnecesidad 'vendor/autoload.php ';uso DiseñoPatternsBookCollection;$books Colección = nuevo BookCollection();$books Colección-addTitle()Patrones de diseño ');$books Colección-addTitle()'PHP7 es el mejor ');$books Colección-addTitle()Reglas de Laravel ');$books Colección-addTitle()Reglas de DHH ');en adelante ()$books Colección como $book) {} var_dump()$book);}

Salida

string(15) "Design Patterns"
string(16) "PHP7 es el mejor"
string(13) "Laravel Rules"
cadena(9) "Reglas DHH"

Pitón

Python prescribe una sintaxis para los iteradores como parte del propio lenguaje, de modo que las palabras clave del lenguaje como for funcionen con lo que Python llama iterables. Un iterable tiene un método __iter__() que devuelve un objeto iterador. El "protocolo iterador" requiere que next() devuelva el siguiente elemento o genere una excepción StopIteration al llegar al final de la secuencia. Los iteradores también proporcionan un método __iter__() que se devuelve a sí mismos para que también se puedan iterar; por ejemplo, usando un bucle for. Los generadores están disponibles desde 2.2.

En Python 3, next() pasó a llamarse __next__().

Raku

Raku proporciona API para iteradores, como parte del propio lenguaje, para objetos que se pueden iterar con for y construcciones de iteración relacionadas, como la asignación a una variable Positional. Un iterable debe implementar al menos un método iterator que devuelva un objeto iterador. El "protocolo iterador" requiere el método pull-one para devolver el siguiente elemento si es posible, o devolver el valor centinela IterationEnd si no se pueden producir más valores. Las API de iteración se proporcionan al componer el rol Iterable, Iterator, o ambos, e implementar los métodos necesarios.

Para verificar si un objeto de tipo o una instancia de objeto es iterable, se puede usar el método does:

Di: Array.¿Sí?()Iterable); #=conocer Cierto.Di: [1, 2, 3].¿Sí?()Iterable); #=conocer Cierto.Di: Str.¿Sí?()Iterable); #=conocer FalsoDi: "Hola".¿Sí?()Iterable); #=conocer Falso

El método does devuelve True si y solo si el invocador se ajusta al tipo de argumento.

Este es un ejemplo de una subrutina range que imita la función range de Python:

multi rango()Int:D $start, Int:D Donación Donde * $start, Int:D Paso Donde * 0 =1♪
()clase {}
 también ¿Sí? Iterable ¿Sí? Iterator;
 tiene Int ()$.start, $.end, $.step);
 tiene Int ¡Cuenta! = ¡$!;

 método iterator {} auto }
 método Tira-uno(... Musi ¡Cuenta!¡Adelante! {}
 # $i = ¡Cuenta!;
 ¡Cuenta! += ¡Paso!;
 retorno $i;
}
 más {}
 ¡Cuenta! = ¡$!;
 retorno IteraciónEnd;
}
}
}).nuevo(:$start,:Donación,:Paso)
}

multi rango()Int:D $start, Int:D Donación Donde * $start, Int:D Paso Donde * 0 = 1♪
()clase {}
 también ¿Sí? Iterable ¿Sí? Iterator;
 tiene Int ()$.start, $.end, $.step);
 tiene Int ¡Cuenta! = ¡$!;

 método iterator {} auto }
 método Tira-uno(... Musi ¡Cuenta!. ¡Adelante! {}
 # $i = ¡Cuenta!;
 ¡Cuenta! += ¡Paso!;
 retorno $i;
}
 más {}
 ¡Cuenta! = ¡$!;
 retorno IteraciónEnd;
}
}
}).nuevo(:$start,:Donación,:Paso)
}

# x = rango()1, 5);
.Di: para x;
# OutPUT:# 1# 2# 3# 4Di: x.mapa(* ** 2).suma;
# OutPUT:# 30# Sí. = rango(-1, -5);
.Di: para Sí.;
# OutPUT:# -1# -2# -3# -4Di: Sí..mapa(* ** 2).suma;
# OutPUT:# 30

Contenido relacionado

RAMDAC

Un convertidor digital a analógico de memoria de acceso aleatorio es una combinación de tres rápidos conversores digital a analógico con una pequeña...

Mente Abierta Sentido Común

Open Mind Common Sense es un proyecto de inteligencia artificial basado en el Media Lab del Instituto Tecnológico de Massachusetts cuyo objetivo es construir...

Pruebas de caja negra

La prueba de caja negra es un método de prueba de software que examina la funcionalidad de una aplicación sin examinar sus estructuras internas o su...
Más resultados...
Tamaño del texto:
undoredo
format_boldformat_italicformat_underlinedstrikethrough_ssuperscriptsubscriptlink
save