Modello di progettazione dell’iteratore in Java

Il pattern di progettazione Iterator è uno dei pattern comportamentali. Il pattern Iterator viene utilizzato per fornire un modo standard per attraversare un gruppo di oggetti. Il pattern Iterator è ampiamente utilizzato nel Java Collection Framework. L’interfaccia Iterator fornisce metodi per attraversare una collezione.

Pattern di progettazione Iterator

Secondo GoF, l’intento del pattern di progettazione Iterator è:

Fornisce un modo per accedere agli elementi di un oggetto aggregato senza esporre la sua rappresentazione sottostante.

Il pattern Iterator non riguarda solo l’attraversamento di una collezione, ma possiamo fornire diversi tipi di iteratori in base alle nostre esigenze. Il pattern di progettazione Iterator nasconde l’implementazione effettiva dell’attraversamento della collezione e i programmi client utilizzano semplicemente i metodi dell’iteratore.

Esempio del Pattern Iterator

Capire il modello iteratore con un semplice esempio. Supponiamo di avere una lista di canali radio e il programma client desidera scorrerli uno per uno o in base al tipo di canale. Ad esempio, alcuni programmi client sono interessati solo ai canali in inglese e vogliono elaborarli solo loro, non vogliono elaborare altri tipi di canali. Quindi possiamo fornire una collezione di canali al client e lasciare che scriva la logica per scorrere i canali e decidere se elaborarli. Ma questa soluzione ha molti problemi, come ad esempio il fatto che il client debba creare la logica per lo scorrimento. Non possiamo essere certi che la logica del client sia corretta. Inoltre, se il numero di client aumenta, diventerà molto difficile da mantenere. Qui possiamo utilizzare il modello iteratore e fornire l’iterazione in base al tipo di canale. Dobbiamo assicurarci che il programma client possa accedere alla lista dei canali solo tramite l’iteratore. La prima parte dell’implementazione consiste nel definire il contratto per le nostre interfacce di raccolta e iteratore. ChannelTypeEnum.java

package com.journaldev.design.iterator;

public enum ChannelTypeEnum {

	ENGLISH, HINDI, FRENCH, ALL;
}

ChannelTypeEnum è un’enum di Java che definisce tutti i diversi tipi di canali. 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 è una semplice classe POJO che ha attributi frequenza e tipo di canale. 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 definisce il contratto per l’implementazione della nostra classe di raccolta. Nota che ci sono metodi per aggiungere e rimuovere un canale, ma non c’è un metodo che restituisce la lista dei canali. ChannelCollection ha un metodo che restituisce l’iteratore per attraversare. L’interfaccia ChannelIterator definisce i seguenti metodi; ChannelIterator.java

package com.journaldev.design.iterator;

public interface ChannelIterator {

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

Ora la nostra interfaccia di base e le classi principali sono pronte, procediamo con l’implementazione della classe di raccolta e dell’iteratore. 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;
		}

	}
}

Nota l’implementazione dell’inner class dell’interfaccia dell’iteratore in modo che l’implementazione non possa essere utilizzata da nessun’altra raccolta. Lo stesso approccio è seguito anche dalle classi di raccolta e tutte hanno un’implementazione di inner class dell’interfaccia Iterator. Scriviamo un semplice programma di test del modello di iteratore per utilizzare la nostra raccolta e l’iteratore per attraversare la raccolta di canali. 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("******");
		 // Iteratore del tipo di canale 
		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;
	}

}

Quando eseguo il programma sopra, produce il seguente output;

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

Punti importanti del modello di design dell’iteratore

  • Il modello di iteratore è utile quando si desidera fornire un modo standard per iterare su una raccolta e nascondere la logica di implementazione dal programma client.
  • La logica per l’iterazione è incorporata nella raccolta stessa e aiuta il programma client a iterare su di esse facilmente.

Modello di progettazione dell’iteratore in JDK

Tutti sappiamo che l’iteratore del framework di raccolta è il miglior esempio di implementazione del modello di iteratore, ma sai che la classe java.util.Scanner implementa anche l’interfaccia Iterator. Leggi questo post per conoscere la Classe Scanner di Java. Questo è tutto per il modello di progettazione dell’iteratore, spero che sia utile e facile da capire.

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