The Iterator Pattern demonstrated using Java Code
Iterator Pattern Diagram
The
Iterator pattern is used to provide a standard way to traverse through a group of Objects.
The
Iterator pattern is used frequently in the Java Collection Framework where the Iterator interface provides methods for traversing through a collection. According to the GoF, the intent of the
Iterator design pattern is:
Provides a way to access the elements of an aggregate object without exposing its underlying represenation.
Iterator pattern is not only about traversing through a collection, we can provide different kinds of iterators based on our requirements.
The
Iterator pattern hides the actual implementation of traversal through the collection and client programs.
Suppose we have a list of Radio channels and the client program wants to traverse through them one by one or based on the type of channel, for example some client programs are only interested in English channels and do not want to process other types of channels.
Provide a Collection of Channels
We can provide a collection of channels to the client and let them write the logic to traverse through the channels and decide whether to process them. But this solution has many challenges such as the client has to come up with the logic for traversal. We cannot insure that the client logic is correct and if the number of clients grows, it will become difficult to maintain. Here we can use the Iterator pattern and provide iteration based on the type of channel. We should make sure that client program can access the list of channels only through the iterator. The first part of the implementation is to define the contract for our collection and iterator interfaces.
// ChannelTypeEnum.java
package com.java.behavioral.iterator;
public enum ChannelTypeEnum {
ENGLISH, GERMAN, FRENCH, ALL;
}
ChannelTypeEnum is java enum that defines all the different types of channels.
package com.java.behavioral.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 is a simple POJO class that contains the attributes
- frequency and
- channel
type.
//ChannelCollection.java
package com.java.behavioral.iterator;
public interface ChannelCollection {
public void addChannel(Channel c);
public void removeChannel(Channel c);
public ChannelIterator iterator(ChannelTypeEnum type);
}
ChannelCollection Interface
The ChannelCollection interface defines the contract for our collection class implementation.
Notice that there are methods to add and remove a channel but there is no method that returns the list of channels and it contains a method that returns the iterator for traversal. The ChannelIterator interface defines following methods;
//ChannelIterator.java
package com.java.behavioral.iterator;
public interface ChannelIterator {
public boolean hasNext();
public Channel next();
}
Now that our base interface and core classes are ready, we can proceed with the implementation of the Collection class and Iterator. Create the client application that will have the logic to parse the user input and pass it to the correct expression and then use the output to generate the user response.
// ChannelCollectionImpl.java
package com.java.behavioral.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 Interface
The iterator interface is an inner class so that the implementation cannot be used by any other collection.
The same approach is followed by the collection classes and all of them use inner class implementations of the Iterator interface.
The following is a Driver Class that uses our collection and iterator to traverse through the collection of channels based on type.
//IteratorPatternDriver.java
package com.java.behavioral.iterator;
public class IteratorPatternDriver {
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.GERMAN));
channels.addChannel(new Channel(100.5, ChannelTypeEnum.FRENCH));
channels.addChannel(new Channel(101.5, ChannelTypeEnum.ENGLISH));
channels.addChannel(new Channel(102.5, ChannelTypeEnum.GERMAN));
channels.addChannel(new Channel(103.5, ChannelTypeEnum.FRENCH));
channels.addChannel(new Channel(104.5, ChannelTypeEnum.ENGLISH));
channels.addChannel(new Channel(105.5, ChannelTypeEnum.GERMAN));
channels.addChannel(new Channel(106.5, ChannelTypeEnum.FRENCH));
return channels;
}
}
Frequency=98.5, Type=ENGLISH
Frequency=99.5, Type=GERMAN
Frequency=100.5, Type=FRENCH
Frequency=101.5, Type=ENGLISH
Frequency=102.5, Type=GERMAN
Frequency=103.5, Type=FRENCH
Frequency=104.5, Type=ENGLISH
Frequency=105.5, Type=GERMAN
Frequency=106.5, Type=FRENCH
******
Frequency=98.5, Type=ENGLISH
Frequency=101.5, Type=ENGLISH
Frequency=104.5, Type=ENGLISH