Итератор. Шаблон проектирования в Java

Итератор – это один из поведенческих шаблонов проектирования. Итератор используется для предоставления стандартного способа обхода группы объектов. Итератор широко используется в фреймворке коллекций Java. Интерфейс итератора предоставляет методы для обхода коллекции.

Шаблон проектирования Итератор

Согласно GoF, намерение шаблона проектирования итератора состоит в следующем:

Предоставляет способ доступа к элементам агрегатного объекта, не раскрывая его внутреннего представления.

Шаблон итератора не только обходит коллекцию, но также может предоставлять различные виды итераторов в зависимости от наших потребностей. Шаблон итератора скрывает фактическую реализацию обхода коллекции, и клиентские программы просто используют методы итератора.

Пример шаблона Итератор

Давайте понимать шаблон итератора на простом примере. Предположим, у нас есть список радиоканалов, и клиентская программа хочет перебирать их один за другим или в зависимости от типа канала. Например, некоторые клиентские программы интересуются только английскими каналами и хотят обрабатывать только их, не желая обрабатывать другие типы каналов. Таким образом, мы можем предоставить клиенту коллекцию каналов и позволить им написать логику перебора каналов и решить, обрабатывать их или нет. Но у этого решения есть много проблем, таких как то, что клиенту придется придумывать логику для перебора. Мы не можем гарантировать, что логика клиента правильна. Кроме того, если количество клиентов увеличивается, то становится очень трудно поддерживать систему. Здесь мы можем использовать шаблон Итератора и предоставить итерацию в зависимости от типа канала. Мы должны убедиться, что клиентская программа может получить доступ к списку каналов только через итератор. Первая часть реализации заключается в определении контракта для наших интерфейсов коллекции и итератора. ChannelTypeEnum.java

package com.journaldev.design.iterator;

public enum ChannelTypeEnum {

	ENGLISH, HINDI, FRENCH, ALL;
}

ChannelTypeEnum – это перечисление Java, которое определяет все различные типы каналов. 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 – это простой класс POJO, который имеет атрибуты частоты и типа канала. 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);
	
}

Интерфейс ChannelCollection определяет контракт для реализации нашего класса коллекции. Обратите внимание, что есть методы для добавления и удаления канала, но нет метода, который возвращает список каналов. У ChannelCollection есть метод, который возвращает итератор для обхода. Интерфейс ChannelIterator определяет следующие методы; ChannelIterator.java

package com.journaldev.design.iterator;

public interface ChannelIterator {

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

Теперь наш базовый интерфейс и основные классы готовы, давайте продолжим с реализацией класса коллекции и итератора. 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;
		}

	}
}

Обратите внимание на внутреннюю реализацию класса итератора, чтобы реализация не могла быть использована другой коллекцией. Такой же подход применяется и к классам коллекций, и все они имеют внутреннюю реализацию интерфейса Iterator. Давайте напишем простую тестовую программу для проверки шаблона итератора, чтобы использовать нашу коллекцию и итератор для обхода коллекции каналов. 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("******");
		// Итератор типа канала
		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;
	}

}

Когда я запускаю указанную выше программу, она выводит следующий результат;

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

Важные моменты шаблона проектирования “Итератор”

  • Шаблон итератора полезен, когда вы хотите предоставить стандартный способ итерации по коллекции и скрыть логику реализации от клиентской программы.
  • Логика для итерации встроена в саму коллекцию, и это помогает клиентской программе легко перебирать их.

Шаблон проектирования Итератор в JDK

Мы все знаем, что Итератор коллекций – это лучший пример реализации шаблона итератора, но вы знаете, что класс java.util.Scanner также реализует интерфейс Iterator. Прочтите этот пост, чтобы узнать о Классе Scanner в Java. Это все, что касается шаблона проектирования итератора, надеюсь, это полезно и легко понятно.

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