Шаблон итератора в одном из поведенческих шаблонов, и он используется для обеспечения стандартного способа обхода группы объектов. Шаблон итератора широко используется в Java Collection Framework, где интерфейс Iterator предоставляет методы для обхода коллекции. Согласно GoF, намерение шаблона проектирования итератора:
Предоставляет способ доступа к элементам агрегатного объекта без раскрытия его базового представления.
Шаблон итератора — это не только обход коллекции, мы можем предоставить различные типы итераторов в зависимости от наших требований. Шаблон итератора скрывает фактическую реализацию обхода через коллекцию, а клиентские программы просто используют методы итератора.
Давайте разберемся с этой моделью на простом примере. Предположим, у нас есть список радиоканалов, и клиентская программа хочет просматривать их один за другим или в зависимости от типа канала, например, некоторые клиентские программы интересуются только английскими каналами и хотят обрабатывать только их, но не хочу обрабатывать другие типы каналов.
Таким образом, мы можем предоставить клиенту набор каналов и позволить им написать логику для обхода каналов и решить, обрабатывать ли их. Но это решение имеет много проблем, таких как клиент должен придумать логику обхода. Мы не можем убедиться в правильности клиентской логики, и если число клиентов будет расти, поддерживать его будет очень сложно.
Здесь мы можем использовать шаблон Iterator и предоставлять итерацию в зависимости от типа канала. Мы должны убедиться, что клиентская программа может получить доступ к списку каналов только через итератор.
Первая часть реализации заключается в определении контракта для нашего интерфейса коллекции и итератора.
ChannelTypeEnum.java
|
1
2
3
4
5
6
|
package com.journaldev.design.iterator;public enum ChannelTypeEnum { ENGLISH, HINDI, FRENCH, ALL;} |
ChannelTypeEnum — это перечисление java, которое определяет все различные типы каналов.
Channel.java
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
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
|
01
02
03
04
05
06
07
08
09
10
11
|
package com.journaldev.design.iterator;public interface ChannelCollection { public void addChannel(Channel c); public void removeChannel(Channel c); public ChannelIterator iterator(ChannelTypeEnum type);} |
Интерфейс ChannelCollection определяет контракт для реализации нашего класса коллекции. Обратите внимание, что есть методы для добавления и удаления канала, но нет метода, который возвращает список каналов, и у него есть метод, который возвращает итератор для обхода. Интерфейс ChannelIterator определяет следующие методы;
ChannelIterator.java
|
1
2
3
4
5
6
7
8
|
package com.journaldev.design.iterator;public interface ChannelIterator { public boolean hasNext(); public Channel next();} |
Теперь наш базовый интерфейс и базовые классы готовы, давайте приступим к реализации класса коллекции и итератора.
ChannelCollectionImpl.java
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
|
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
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
|
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("******"); // Channel Type Iterator 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; }} |
Когда я запускаю вышеуказанную программу, она выдает следующий вывод;
IteratorPatternTest.java
|
01
02
03
04
05
06
07
08
09
10
11
12
13
|
Frequency=98.5, Type=ENGLISHFrequency=99.5, Type=HINDIFrequency=100.5, Type=FRENCHFrequency=101.5, Type=ENGLISHFrequency=102.5, Type=HINDIFrequency=103.5, Type=FRENCHFrequency=104.5, Type=ENGLISHFrequency=105.5, Type=HINDIFrequency=106.5, Type=FRENCH******Frequency=98.5, Type=ENGLISHFrequency=101.5, Type=ENGLISHFrequency=104.5, Type=ENGLISH |
Важные точки
- Шаблон итератора полезен, когда вы хотите предоставить стандартный способ перебора коллекции и скрыть логику реализации от клиентской программы.
- Логика итерации встроена в саму коллекцию и помогает клиентской программе легко выполнять итерации по ним.
Шаблон итератора в JDK
Мы все знаем, что Framework Framework Iterator является лучшим примером реализации шаблона итератора, но знаете ли вы, что класс java.util.Scanner также реализует интерфейс Iterator. Прочтите этот пост, чтобы узнать о классе Java Scanner.
Это все для шаблона итератора, я надеюсь, что он полезен и прост для понимания.