Padrão de Projeto Iterador em Java

Iterator é um padrão de design que faz parte dos padrões comportamentais. O padrão Iterator é utilizado para fornecer uma maneira padronizada de percorrer um grupo de objetos. O padrão Iterator é amplamente utilizado no Java Collection Framework. A interface Iterator fornece métodos para percorrer uma coleção.

Padrão de Design Iterator

De acordo com o GoF, a intenção do padrão de design Iterator é:

Fornecer uma maneira de acessar os elementos de um objeto agregado sem expor sua representação subjacente.

O padrão Iterator não se limita apenas a percorrer uma coleção; podemos fornecer diferentes tipos de iteradores com base em nossos requisitos. O padrão Iterator oculta a implementação real da travessia pela coleção, e os programas clientes apenas utilizam os métodos do iterador.

Exemplo do Padrão Iterator

Vamos entender o padrão do iterador com um exemplo simples. Suponha que tenhamos uma lista de canais de rádio e o programa cliente deseja percorrê-los um por um ou com base no tipo de canal. Por exemplo, alguns programas clientes estão interessados apenas em canais em inglês e desejam processar apenas eles, sem querer processar outros tipos de canais. Então podemos fornecer uma coleção de canais ao cliente e permitir que eles escrevam a lógica para percorrê-los e decidir se devem processá-los. Mas essa solução tem muitos problemas, como o cliente ter que criar a lógica para a travessia. Não podemos garantir que a lógica do cliente está correta. Além disso, se o número de clientes aumentar, será muito difícil manter. Aqui podemos usar o padrão do iterador e fornecer iteração com base no tipo de canal. Devemos garantir que o programa cliente possa acessar a lista de canais apenas através do iterador. A primeira parte da implementação é definir o contrato para nossas interfaces de coleção e iterador. `ChannelTypeEnum.java`

package com.journaldev.design.iterator;

public enum ChannelTypeEnum {

	ENGLISH, HINDI, FRENCH, ALL;
}

ChannelTypeEnum é um enum java que define todos os diferentes tipos de canais. `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 é uma classe POJO simples que possui atributos frequência e 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);
	
}

A interface ChannelCollection define o contrato para a implementação de nossa classe de coleção. Observe que existem métodos para adicionar e remover um canal, mas não há um método que retorne a lista de canais. ChannelCollection possui um método que retorna o iterador para travessia. A interface ChannelIterator define os seguintes métodos; ChannelIterator.java

package com.journaldev.design.iterator;

public interface ChannelIterator {

	public boolean hasNext();
	
	public Channel next();
}

Agora que nossa interface base e classes principais estão prontas, vamos prosseguir com a implementação da classe de coleção e do 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;
		}

	}
}

Observe a implementação da classe interna de iterador para que a implementação não possa ser usada por nenhuma outra coleção. A mesma abordagem é seguida pelas classes de coleção e todas elas têm uma implementação de classe interna da interface Iterator. Vamos escrever um programa de teste padrão de iterador simples para usar nossa coleção e iterador para percorrer a coleção de canais. 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;
	}

}

Ao executar o programa acima, ele produz a seguinte saída;

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

Pontos Importantes do Padrão de Projeto Iterador

  • O padrão de projeto Iterador é útil quando você deseja fornecer uma maneira padrão de iterar sobre uma coleção e ocultar a lógica de implementação do programa cliente.
  • A lógica para iteração está embutida na própria coleção e ajuda o programa cliente a iterar sobre eles facilmente.

Padrão de Projeto Iterator no JDK

Todos nós sabemos que o Iterator do framework de Coleção é o melhor exemplo de implementação do padrão iterator, mas você sabia que a classe java.util.Scanner também implementa a interface Iterator? Leia este post para aprender sobre Classe Scanner do Java. Isso é tudo para o padrão de projeto iterator, espero que seja útil e fácil de entender.

Source:
https://www.digitalocean.com/community/tutorials/iterator-design-pattern-java