1. ¿Qué es el patrón de iterador
El patrón de iterador es un patrón de diseño de comportamiento que proporciona un método uniforme para recorrer los elementos de un objeto agregado sin exponer la representación interna del objeto agregado.
2. Características y ventajas del patrón de iterador
Las características y ventajas del patrón de iterador son las siguientes:
- Puede ocultar la estructura interna del objeto de la colección, desacoplando el algoritmo de recorrido del objeto de la colección.
- Proporciona una forma estandarizada de recorrer diferentes tipos de objetos de colección.
- Simplifica el código del cliente, haciéndolo más claro y conciso.
- Puede proporcionar diferentes implementaciones de iteradores para adaptarse a diferentes necesidades de recorrido.
3. Ejemplos de aplicaciones prácticas del patrón de iterador
El patrón de iterador tiene muchas aplicaciones prácticas, tales como:
- Recorrer un conjunto de resultados de consultas de base de datos.
- Recorrer archivos y carpetas en un sistema de archivos.
- Recorrer elementos en una colección.
4. Implementación del patrón de iterador en Golang
4.1 Diagrama de clases UML
4.2 Introducción del ejemplo
En el diagrama de clases UML anterior, tenemos dos roles principales: Iterador y Colección.
- El Iterador define la interfaz para recorrer un objeto de colección, incluyendo el método
HasNext()
para determinar si hay otro elemento y el métodoNext()
para obtener el siguiente elemento. - El ConcreteIterator es la clase de implementación específica de Iterator, que implementa los métodos
HasNext()
yNext()
. - La Colección define la interfaz para crear objetos iteradores, con el método
CreateIterator()
. - El ConcreteCollection es la clase de implementación específica de Collection, que implementa el método
CreateIterator()
.
4.3 Pasos de implementación
A continuación, implementaremos el patrón de iterador en Golang paso a paso.
4.3.1 Definir la interfaz del Iterador y la clase de Iterador Concreto
type Iterator interface {
HasNext() bool
Next() interface{}
}
type ConcreteIterator struct {
collection *ConcreteCollection
index int
}
func (it *ConcreteIterator) HasNext() bool {
if it.index < len(it.collection.items) {
return true
}
return false
}
func (it *ConcreteIterator) Next() interface{} {
if it.HasNext() {
item := it.collection.items[it.index]
it.index++
return item
}
return nil
}
4.3.2 Definir la interfaz del objeto iterable y la clase de objeto iterable concreto
type Collection interface {
CreateIterator() Iterator
}
type ConcreteCollection struct {
items []interface{}
}
func (c *ConcreteCollection) CreateIterator() Iterator {
return &ConcreteIterator{
collection: c,
index: 0,
}
}
4.3.3 Implementar la lógica de generación del iterador en la clase de objeto iterable
func main() {
collection := &ConcreteCollection{
items: []interface{}{"Golang", "Python", "Java"},
}
iterator := collection.CreateIterator()
for iterator.HasNext() {
item := iterator.Next()
fmt.Println(item)
}
}
Ejecutar el código anterior producirá la siguiente salida:
Golang
Python
Java
En el código anterior, definimos una clase ConcreteCollection
que implementa la interfaz Collection
, con su método CreateIterator()
devolviendo un objeto iterador. Utilizamos este objeto iterador en la función main()
para el recorrido.
4.4 Extensión de paso de implementación: Simplificación de la implementación del iterador utilizando la función generadora
En Golang, podemos simplificar la implementación de iteradores utilizando una función generadora (yield
). Aquí hay un ejemplo de cómo usar una función generadora:
func GenerateItems() <-chan interface{} {
items := []interface{}{"Golang", "Python", "Java"}
out := make(chan interface{})
go func() {
defer close(out)
for _, item := range items {
out <- item
}
}()
return out
}
func main() {
for item := range GenerateItems() {
fmt.Println(item)
}
}
En el código anterior, definimos una función GenerateItems()
que devuelve un canal de solo lectura (<-chan
), y utilizamos yield
para enviar secuencialmente elementos al canal dentro de esta función. En la función main()
, usamos range
para recorrer este canal de solo lectura y mostrar los valores de los elementos.
De esta manera, hemos simplificado la implementación del iterador utilizando una función generadora.