El patrón de diseño del Iterador es uno de los patrones de comportamiento. Se utiliza para proporcionar una forma estándar de recorrer un grupo de objetos. El patrón del Iterador se utiliza ampliamente en el Marco de Colecciones de Java. La interfaz del Iterador proporciona métodos para recorrer una colección.
Patrón de Diseño del Iterador
Según GoF, la intención del patrón de diseño del iterador es:
Proporcionar una forma de acceder a los elementos de un objeto agregado sin exponer su representación subyacente.
El patrón del Iterador no se trata solo de recorrer una colección, podemos proporcionar diferentes tipos de iteradores según nuestros requisitos. El patrón de diseño del Iterador oculta la implementación real del recorrido de la colección y los programas cliente solo utilizan los métodos del iterador.
Ejemplo del Patrón del Iterador
Entendamos el patrón del iterador con un ejemplo simple. Supongamos que tenemos una lista de canales de radio y el programa cliente desea recorrerlos uno por uno o según el tipo de canal. Por ejemplo, algunos programas clientes solo están interesados en los canales en inglés y desean procesar solo esos, sin procesar otros tipos de canales. Así que podemos proporcionar una colección de canales al cliente y dejar que ellos escriban la lógica para recorrer los canales y decidir si procesarlos. Pero esta solución tiene muchos problemas, como que el cliente tiene que idear la lógica para el recorrido. No podemos asegurarnos de que la lógica del cliente sea correcta. Además, si el número de clientes aumenta, entonces será muy difícil de mantener. Aquí es donde podemos usar el patrón del Iterador y proporcionar iteración basada en el tipo de canal. Debemos asegurarnos de que el programa cliente solo pueda acceder a la lista de canales a través del iterador. La primera parte de la implementación es definir el contrato para nuestras interfaces de colección e iterador. `ChannelTypeEnum.java
` `
package com.journaldev.design.iterator;
public enum ChannelTypeEnum {
ENGLISH, HINDI, FRENCH, ALL;
}
`ChannelTypeEnum es `java enum` que define todos los diferentes tipos de canales. `Channel.java
` `
package com.journaldev.design.iterator;
public class Channel {
private double frequency;
private ChannelTypeEnum TYPE;
public Channel(double freq, ChannelTypeEnum type){
this.frequency=freq;
this.TYPE=type;
}
public double getFrequency() {
return frequency;
}
public ChannelTypeEnum getTYPE() {
return TYPE;
}
@Override
public String toString(){
return "Frequency="+this.frequency+", Type="+this.TYPE;
}
}
`Channel es una clase POJO simple que tiene atributos de frecuencia y tipo de canal. `ChannelCollection.java
`
package com.journaldev.design.iterator;
public interface ChannelCollection {
public void addChannel(Channel c);
public void removeChannel(Channel c);
public ChannelIterator iterator(ChannelTypeEnum type);
}
La interfaz ChannelCollection define el contrato para la implementación de nuestra clase de colección. Tenga en cuenta que hay métodos para agregar y eliminar un canal, pero no hay un método que devuelva la lista de canales. ChannelCollection tiene un método que devuelve el iterador para el recorrido. La interfaz ChannelIterator define los siguientes métodos; ChannelIterator.java
package com.journaldev.design.iterator;
public interface ChannelIterator {
public boolean hasNext();
public Channel next();
}
Ahora nuestras interfaces base y clases principales están listas, procedamos con la implementación de la clase de colección y del iterador. ChannelCollectionImpl.java
package com.journaldev.design.iterator;
import java.util.ArrayList;
import java.util.List;
public class ChannelCollectionImpl implements ChannelCollection {
private List<Channel> channelsList;
public ChannelCollectionImpl() {
channelsList = new ArrayList<>();
}
public void addChannel(Channel c) {
this.channelsList.add(c);
}
public void removeChannel(Channel c) {
this.channelsList.remove(c);
}
@Override
public ChannelIterator iterator(ChannelTypeEnum type) {
return new ChannelIteratorImpl(type, this.channelsList);
}
private class ChannelIteratorImpl implements ChannelIterator {
private ChannelTypeEnum type;
private List<Channel> channels;
private int position;
public ChannelIteratorImpl(ChannelTypeEnum ty,
List<Channel> channelsList) {
this.type = ty;
this.channels = channelsList;
}
@Override
public boolean hasNext() {
while (position < channels.size()) {
Channel c = channels.get(position);
if (c.getTYPE().equals(type) || type.equals(ChannelTypeEnum.ALL)) {
return true;
} else
position++;
}
return false;
}
@Override
public Channel next() {
Channel c = channels.get(position);
position++;
return c;
}
}
}
Tenga en cuenta la implementación de la clase interna de la interfaz del iterador para que la implementación no pueda ser utilizada por ninguna otra colección. El mismo enfoque también es seguido por las clases de colección y todas tienen implementación de clase interna de la interfaz Iterator. Escribamos un programa de prueba simple del patrón iterador para usar nuestra colección e iterador para recorrer la colección de canales. IteratorPatternTest.java
package com.journaldev.design.iterator;
public class IteratorPatternTest {
public static void main(String[] args) {
ChannelCollection channels = populateChannels();
ChannelIterator baseIterator = channels.iterator(ChannelTypeEnum.ALL);
while (baseIterator.hasNext()) {
Channel c = baseIterator.next();
System.out.println(c.toString());
}
System.out.println("******");
// Iterador de tipo de canal
ChannelIterator englishIterator = channels.iterator(ChannelTypeEnum.ENGLISH);
while (englishIterator.hasNext()) {
Channel c = englishIterator.next();
System.out.println(c.toString());
}
}
private static ChannelCollection populateChannels() {
ChannelCollection channels = new ChannelCollectionImpl();
channels.addChannel(new Channel(98.5, ChannelTypeEnum.ENGLISH));
channels.addChannel(new Channel(99.5, ChannelTypeEnum.HINDI));
channels.addChannel(new Channel(100.5, ChannelTypeEnum.FRENCH));
channels.addChannel(new Channel(101.5, ChannelTypeEnum.ENGLISH));
channels.addChannel(new Channel(102.5, ChannelTypeEnum.HINDI));
channels.addChannel(new Channel(103.5, ChannelTypeEnum.FRENCH));
channels.addChannel(new Channel(104.5, ChannelTypeEnum.ENGLISH));
channels.addChannel(new Channel(105.5, ChannelTypeEnum.HINDI));
channels.addChannel(new Channel(106.5, ChannelTypeEnum.FRENCH));
return channels;
}
}
Cuando ejecuto el programa anterior, produce la siguiente salida;
Frequency=98.5, Type=ENGLISH
Frequency=99.5, Type=HINDI
Frequency=100.5, Type=FRENCH
Frequency=101.5, Type=ENGLISH
Frequency=102.5, Type=HINDI
Frequency=103.5, Type=FRENCH
Frequency=104.5, Type=ENGLISH
Frequency=105.5, Type=HINDI
Frequency=106.5, Type=FRENCH
******
Frequency=98.5, Type=ENGLISH
Frequency=101.5, Type=ENGLISH
Frequency=104.5, Type=ENGLISH
Puntos Importantes del Patrón de Diseño del Iterador
- El patrón de iterador es útil cuando se desea proporcionar una forma estándar de iterar sobre una colección y ocultar la lógica de implementación del programa cliente.
- La lógica para la iteración está incrustada en la propia colección y ayuda al programa cliente a iterar sobre ellas fácilmente.
Patrón de diseño Iterator en JDK
Todos sabemos que el Iterador del framework de Colecciones es el mejor ejemplo de implementación del patrón iterador, pero ¿sabías que la clase java.util.Scanner
también implementa la interfaz Iterator? Lee esta publicación para aprender sobre la Clase Scanner de Java. Eso es todo sobre el patrón de diseño iterador, espero que sea útil y fácil de entender.
Source:
https://www.digitalocean.com/community/tutorials/iterator-design-pattern-java