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 de 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 puede implementarse generalmente usando un tipo específico de iterador en lugar de implementarlo como un algoritmo específico de contenedor. Esto permite que SearchForElement se utilice en cualquier contenedor que admita el tipo de iterador requerido.
Descripción general
El patrón de diseño Iterator [1] es uno de los veintitrés patrones de diseño GoF bien conocidos 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 del iterador? [2]
- Se debe acceder y recorrer los elementos de un objeto agregado sin exponer su representación (estructuras de datos).
- Se deben definir nuevas operaciones transversales 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 operaciones de acceso y cruce en particular y hace que sea imposible agregar nuevas operaciones más adelante sin tener que cambiar la interfaz agregada.
¿Qué solución describe el patrón de diseño del iterador?
- Defina un objeto separado (iterador) que encapsule el acceso y el recorrido de un objeto agregado.
- Los clientes utilizan un iterador para acceder y recorrer un agregado sin conocer su representación (estructuras de datos).
Se pueden usar diferentes iteradores para acceder y atravesar un agregado de diferentes maneras.
Se pueden definir nuevas operaciones de acceso y recorrido de forma independiente mediante la definición de nuevos iteradores.
Consulte también el diagrama de secuencia y clase UML a continuación.
Definición
La esencia del patrón de iterador es "Proporcionar una forma de acceder a los elementos de un objeto agregado secuencialmente sin exponer su representación subyacente". [3]
Estructura
Diagrama de secuencia y clase UML
En el diagrama de clases de UML anterior , la Client
clase se refiere (1) a la Aggregate
interfaz para crear un Iterator
objeto ( createIterator()
) y (2) a la Iterator
interfaz para atravesar un Aggregate
objeto ( next(),hasNext()
). La Iterator1
clase implementa la Iterator
interfaz accediendo a la Aggregate1
clase.
El diagrama de secuencia UML muestra las interacciones en tiempo de ejecución: el Client
objeto llama createIterator()
a un Aggregate1
objeto, que crea un Iterator1
objeto y lo devuelve al archivo Client
. Los Client
usos luego Iterator1
para atravesar los elementos del Aggregate1
objeto.
Diagrama de clases UML
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
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
interfaz. Desde C # v2, foreach
también puede iterar a través de tipos que implementan System.Collections.Generic.IEnumerable
y System.Collections.Generic.IEnumerator
[5]
Ejemplo de foreach
declaración de uso :
var primes = new List < int > { 2 , 3 , 5 , 7 , 11 , 13 , 17 , 19 }; largo m = 1 ; foreach ( var p en números primos ) m * = p ;
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 decremento. Esto tiene la ventaja de que los algoritmos de C ++, como los que std::sort
se pueden aplicar inmediatamente a los búferes de memoria antiguos, y que no hay una nueva sintaxis que aprender. Sin embargo, requiere un iterador "final" 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 .
Java
Java tiene la Iterator
interfaz.
Un ejemplo simple que muestra cómo devolver números enteros entre [inicio, final] usando un Iterator
import java.util.Iterator ; import java.util.NoSuchElementException ;public class RangeIteratorExample { public static Iterator < Integer > range ( int start , int end ) { return new Iterator <> () { private int index = start ; @Override public boolean hasNext () { índice de retorno < fin ; } @Override public Integer next () { if ( ! HasNext ()) { lanzar nueva NoSuchElementException (); } índice de retorno ++ ; } }; } public static void main ( String [] args ) { var iterator = range ( 0 , 10 ); while ( iterador . hasNext ()) { System . fuera . println ( iterador . siguiente ()); } // o usando un iterador lambda . forEachRemaining ( System . out :: println ); } }
A partir de Java 5, los objetos que implementan la Iterable
interfaz, que devuelve un Iterator
de su único método, se pueden atravesar utilizando la sintaxis de bucle foreach de Java . Se amplía la Collection
interfaz del marco de colecciones de JavaIterable
.
Ejemplo de clase que Family
implementa la Iterable
interfaz:
import java.util.Iterator ; import java.util.Set ;clase Familia < E > implementos Iterable < E > { privado última Conjunto < E > elementos ; pública Familia ( Set < E > elementos ) { esto . elementos = Establecer . copyOf ( elementos ); } @ Override pública Iterator < E > iterador ) ( { retorno elementos . iterador (); } }
La clase IterableExample
demuestra el uso de la clase Family
:
public class IterableExample { public static void main ( String [] args ) { var weasleys = Set . de ( "Arthur" , "Molly" , "Bill" , "Charlie" , "Percy" , "Fred" , "George" , "Ron" , "Ginny" ); var familia = nueva familia <> ( weasleys ); para ( nombre var : familia ) { System . fuera . println ( nombre + "Weasley" ); } } }
Producción:
Ron WeasleyMolly WeasleyPercy WeasleyFred WeasleyCharlie WeasleyGeorge WeasleyArthur WeasleyGinny WeasleyBill Weasley
JavaScript
JavaScript , como parte de ECMAScript 6 , admite el patrón de iterador con cualquier objeto que proporcione un next()
método, que devuelve un objeto con dos propiedades específicas: done
y value
. Aquí hay un ejemplo que muestra un iterador de matriz inverso:
function reverseArrayIterator ( matriz ) { índice var = matriz . longitud - 1 ; return { siguiente : () => índice > = 0 ? { valor : matriz [ índice - ], hecho : falso } : { hecho : verdadero } } } const it = reverseArrayIterator ([ 'tres' , 'dos' , 'uno' ]); consola . log ( it . next (). valor ); // -> 'una' consola . log ( it . next (). valor ); // -> consola 'dos' . log ( it . next (). valor ); // -> consola 'tres' . log ( `¿Terminaste? $ { it . next (). done } ` ); // -> cierto
La mayoría de las veces, sin embargo, es deseable proporcionar semántica de Iterator [6] en los objetos para que puedan ser iterados automáticamente a través de for...of
bucles. Algunos de JavaScript incorporado en tipos tales como Array
, Map
o Set
ya definir su propio comportamiento iteración. El mismo efecto se puede lograr definiendo el meta @@iterator
método de un objeto , también conocido por Symbol.iterator
. Esto crea un objeto Iterable.
Aquí hay un ejemplo de una función de rango que genera una lista de valores comenzando desde start
hasta end
, exclusivo, usando un for
ciclo regular para generar los números:
rango de función ( inicio , fin ) { retorno { [ Símbolo . iterador ] () { // #A devuelve esto ; }, siguiente () { si ( inicio < fin ) { retorno { valor : inicio ++ , hecho : falso }; // #B } return { hecho : verdadero , valor : final }; // #B } } }para ( número de rango ( 1 , 5 )) { consola . log ( número ); // -> 1, 2, 3, 4 }
El mecanismo de iteración de los tipos integrados, como las cadenas, también se puede manipular:
let iter = [ 'I' , 't' , 'e' , 'r' , 'a' , 't' , 'o' , 'r' ] [ Symbol . iterador ] (); iter . siguiente (). valor ; // -> Yo iter . 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. [7] Los objetos que implementan la interfaz se pueden iterar con la foreach
construcción del lenguaje.
Ejemplo de patrones usando PHP:
php// BookIterator.php DesignPatterns del espacio de nombres ;class BookIterator implementa \ Iterator { privado int $ i_position = 0 ; colección de libros privada $ colección de libros ; función pública __construct ( Colección de libros $ colección de libros ) { $ esto -> colección de libros = $ colección de libros ; } public function current () { return $ this -> booksCollection -> getTitle ( $ this -> i_position ); } tecla de función pública () : int { return $ this -> i_position ; } función pública next () : void { $ this -> i_position ++ ; } función pública rewind () : void { $ this -> i_position = 0 ; } función pública valida () : bool { return ! is_null ( $ this -> booksCollection -> getTitle ( $ this -> i_position )); } }
php// BookCollection.php DesignPatterns del espacio de nombres ;class BookCollection implementa \ IteratorAggregate { arreglo privado $ a_titles = arreglo (); pública función getIterator () { devolver nueva BookIterator ( $ this ); } función pública addTitle ( string $ string ) : void { $ this -> a_titles [] = $ string ; } función pública getTitle ( $ key ) { if ( isset ( $ this -> a_titles [ $ key ])) { return $ this -> a_titles [ $ key ]; } devolver nulo ; } función pública is_empty () : bool { return empty ( $ this -> $ a_titles ); } }
php// index.phprequiere 'vendor / autoload.php' ; use DesignPatterns \ BookCollection ;$ colección de libros = nueva colección de libros (); $ booksCollection -> addTitle ( 'Patrones de diseño' ); $ booksCollection -> addTitle ( 'PHP7 es el mejor' ); $ booksCollection -> addTitle ( 'Reglas de Laravel' ); $ booksCollection -> addTitle ( 'Reglas DHH' );foreach ( $ colección de libros como $ libro ) { var_dump ( $ libro ); }
Producción
string (15) "Patrones de diseño"string (16) "PHP7 es el mejor"string (13) "Reglas de Laravel"string (9) "Reglas DHH"
Pitón
Python prescribe una sintaxis para iteradores como parte del lenguaje en sí, de modo que las palabras clave del lenguaje como for
funcionan con lo que Python llama iterables. Un iterable tiene un __iter__()
método que devuelve un objeto iterador. El "protocolo iterador" requiere next()
devolver el siguiente elemento o generar una StopIteration
excepción al llegar al final de la secuencia. Los iteradores también proporcionan un __iter__()
método que regresa a sí mismos para que también se puedan iterar; por ejemplo, usando un for
bucle. Los generadores están disponibles desde 2.2.
En Python 3, next()
se cambió el nombre __next__()
. [8]
Raku
Raku proporciona API para iteradores, como parte del propio lenguaje, para objetos con los que se puede iterar for
y construcciones de iteración relacionadas, como la asignación a una Positional
variable. [9] [10] Un iterable debe al menos implementar un iterator
método que devuelva un objeto iterador. El "protocolo iterador" requiere que el pull-one
método devuelva el siguiente elemento si es posible, o devuelva el valor centinela IterationEnd
si no se pueden producir más valores. Las API de iteración se proporcionan componiendo el Iterable
rol Iterator
, o ambos, e implementando los métodos requeridos.
Para comprobar si un objeto de tipo o una instancia de objeto es iterable, does
se puede utilizar el método:
diga Array . hace ( Iterable ); # => Verdadero dice [ 1 , 2 , 3 ]. hace ( Iterable ); # => Verdadero dice Str . hace ( Iterable ); # => Falso decir "Hola" . hace ( Iterable ); # => Falso
El does
método devuelve True
si y solo si el invocador se ajusta al tipo de argumento.
Aquí hay un ejemplo de una range
subrutina que imita la range
función de Python :
rango múltiple ( Int: D $ inicio , Int: D $ final donde * <= $ inicio , Int: D $ paso donde * < 0 = - 1 ) { ( class { también hace Iterable hace Iterador ; tiene Int ( $ .start , $ .end , $ .step ); tiene Int $! count = $! start ; método iterador { self } método pull-one (-> Mu ) { if $! count > $! end { my $ i = $! count ; $! contar + = $! paso ; return $ i ; } else { $! count = $! start ; return IterationEnd ; } } }). nuevo (: $ inicio ,: $ final ,: $ paso )} rango múltiple ( Int: D $ inicio , Int: D $ final donde *> = $ inicio , Int: D $ paso donde *> 0 = 1 ) { ( class { también hace Iterable hace Iterador ; tiene Int ( $ .start , $ .end , $ .step ); tiene Int $! count = $! start ; método iterador { self } método pull-one (-> Mu ) { if $! count < $! end { my $ i = $! count ; $! contar + = $! paso ; return $ i ; } else { $! count = $! start ; return IterationEnd ; } } }). nuevo (: $ inicio ,: $ final ,: $ paso )}mi \ x = rango ( 1 , 5 );. decir por x ;# SALIDA: # 1 # 2 # 3 # 4decir x . mapa (* ** 2 ). Resumiendo ;# SALIDA: # 30mi \ y = rango (- 1 , - 5 );. decir por y ;# SALIDA: # -1 # -2 # -3 # -4decir y . mapa (* ** 2 ). Resumiendo ;# SALIDA: # 30
Ver también
Referencias
- ^ Erich Gamma; Richard Helm; Ralph Johnson; John Vlissides (1994). Patrones de diseño: elementos de software orientado a objetos reutilizable . Addison Wesley. págs. 257ff . ISBN 0-201-63361-2.
- ^ "El patrón de diseño del iterador: problema, solución y aplicabilidad" . w3sDesign.com . Consultado el 12 de agosto de 2017 .
- ^ Banda de cuatro
- ^ "El patrón de diseño del iterador - Estructura y colaboración" . w3sDesign.com . Consultado el 12 de agosto de 2017 .
- ^ "C # sentencia foreach" .
- ^ "Iteradores y generadores" . Consultado el 18 de marzo de 2016 .
- ^ "PHP: Iterador" . Consultado el 23 de junio de 2013 .
- ^ "Documentación de Python v2.7.1: La biblioteca estándar de Python: 5. Tipos integrados" . Consultado el 2 de mayo de 2011 .
- ^ "Documentación de Raku: rol Iterable" . Consultado el 9 de diciembre de 2020 .
- ^ "Documentación de Raku: iterador de roles" . Consultado el 9 de diciembre de 2020 .
enlaces externos
- Iteración de objetos en PHP
- Patrón de iterador en C #
- Patrón de iterador en UML y en LePUS3 (un lenguaje de modelado formal)
- Tutorial de SourceMaking
- Tutorial de ejemplos de implementación de patrones de diseño
- Patrón de iterador