En la programación orientada a objetos , el patrón iterador es un patrón de diseño en el que se utiliza un iterador para recorrer 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 se puede implementar de forma general utilizando un tipo específico de iterador en lugar de implementarlo como un algoritmo específico del contenedor. Esto permite que SearchForElement se utilice en cualquier contenedor que admita el tipo de iterador requerido.
El patrón de diseño Iterador [1] es uno de los 23 patrones de diseño conocidos del "Banda de los Cuatro" 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.
[2]
La definición de operaciones de acceso y recorrido en la interfaz agregada es inflexible porque compromete el agregado con operaciones de acceso y recorrido particulares y hace imposible agregar nuevas operaciones más tarde sin tener que cambiar la interfaz agregada.
Se pueden utilizar distintos iteradores para acceder y recorrer un agregado de distintas maneras.
Se pueden definir nuevas operaciones de acceso y recorrido de forma independiente mediante la definición de nuevos iteradores.
Vea también el diagrama de clases y secuencias UML a continuación.
La esencia del patrón Iterador es "Proporcionar una forma de acceder a los elementos de un objeto agregado secuencialmente sin exponer su representación subyacente". [3]
En el diagrama de clases UML anterior , la clase hace referencia (1) a la interfaz para crear un objeto ( ) y (2) a la interfaz para recorrer un objeto ( ). La clase implementa la interfaz accediendo a la clase.Client
Aggregate
Iterator
createIterator()
Iterator
Aggregate
next(),hasNext()
Iterator1
Iterator
Aggregate1
El diagrama de secuencia UML
muestra las interacciones en tiempo de ejecución: el objeto llama a un objeto, que crea un objeto y lo devuelve al . Luego, utiliza para recorrer los elementos del objeto.Client
createIterator()
Aggregate1
Iterator1
Client
Client
Iterator1
Aggregate1
Algunos lenguajes estandarizan la sintaxis. C++ y Python son ejemplos notables.
C++ implementa iteradores con la semántica de los punteros de 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, con desreferencia, incremento y decremento incluidos. Esto tiene la ventaja de que los algoritmos de C++ como std::sort
pueden aplicarse inmediatamente a los buffers de memoria tradicionales y que no hay una nueva sintaxis que aprender. Sin embargo, requiere un iterador "final" para comprobar la igualdad, en lugar de permitir que un iterador sepa que ha llegado al final. En el lenguaje C++, decimos que un iterador modela el concepto de iterador .
Esta implementación de C++11 se basa en el capítulo "Generalizando el vector una vez más". [5]
#include <iostream> #include <stdexcept> #include <lista_inicializador> clase Vector { público : using iterador = double * ; iterador begin () { devolver elem ; } iterador end () { devolver elem + sz ; } Vector ( std :: lista_inicializador < double > lst ) : elem ( nullptr ), sz ( 0 ) { sz = lst.size ( ); elem = new double [ sz ]; double * p = elem ; for ( auto i = lst.begin (); i ! = lst.end ( ) ; ++ i , ++ p ) { * p = * i ; } } ~ Vector ( ) { eliminar [ ] elem ; } int tamaño () const { devolver sz ; } operador doble & []( int n ) { si ( n < 0 || n >= sz ) arrojar std :: fuera_de_rango ( "Vector::operador[]" ); devolver elem [ n ]; } Vector ( const Vector & ) = eliminar ; // regla de tres Operador Vector & = ( const Vector & ) = eliminar ; privado : doble * elem ; int sz ; }; int main () { Vector v = { 1.1 * 1.1 , 2.2 * 2.2 }; para ( const auto & x : v ) { std :: cout << x << '\n' ; } para ( auto i = v . begin (); i != v . end (); ++ i ) { std :: cout << * i << '\n' ; } para ( auto i = 0 ; i <= v . size (); ++ i ) { std :: cout << v [ i ] << '\n' ; } }
La salida del programa es
1.21 4.84 1.21 4.84 1.21 4.84 terminar llamado después de lanzar una instancia de ' std :: out_of_range ' what () : Vector :: operator []