Padrão de Projeto Iterator em Java

Padrão de design Iterator é um dos padrões comportamentais. O padrão Iterator é usado para fornecer uma maneira padrão 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 GoF, a intenção do padrão de design iterator é:

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

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

Exemplo do Padrão Iterator

Vamos entender o padrão de iterador com um exemplo simples. Suponha que tenhamos uma lista de canais de rádio e o programa do cliente deseja percorrê-los um por um ou com base no tipo de canal. Por exemplo, alguns programas de clientes estão interessados apenas em canais em inglês e desejam processar apenas esses; eles não querem processar outros tipos de canais. Portanto, podemos fornecer uma coleção de canais ao cliente e permitir que eles escrevam a lógica para percorrer os canais e decidir se devem processá-los. No entanto, 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 esteja correta. Além disso, se o número de clientes aumentar, será muito difícil de manter. Aqui, podemos usar o padrão de Iterador e fornecer iteração com base no tipo de canal. Devemos garantir que o programa do cliente possa acessar a lista de canais apenas por meio 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 em 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 tem os 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);
	
}

interface ChannelCollection define o contrato para a implementação da nossa classe de coleção. Observe que existem métodos para adicionar e remover um canal, mas não há um método que retorna 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 do iterador para que a implementação não possa ser usada por nenhuma outra coleção. A mesma abordagem é seguida também pelas classes de coleção, e todas elas têm a implementação da classe interna da interface Iterator. Vamos escrever um programa de teste simples para o padrão de iterador usando 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 do 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 Design do Iterador

  • O padrão de 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 de iteração está incorporada na própria coleção e ajuda o programa cliente a iterar sobre elas facilmente.

Padrão de Projeto Iterator no JDK

Todos sabemos que o Iterator do framework de Coleções é 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 Java Scanner. 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