How to implement Iterator pattern in java?

Photo by Keegan Everitt on Pexels.com

Let’s say you run a small theatre.

Your theatre is unique , only those who have membership can watch movies in your theatre.

Anyone of any age can be a member.

You are not just screening latest movies but curated collections tailored for movie lovers.

You have a database of all members . There are men , women and kids too!

Now you want to iterate through all the men in your database , and likewise for women and kids and print their details.

Ofcourse this has a straightforward implementation.

Java already provides good collection data structures to store your member data : List, Map , Trees etc.

You can choose List Collection , add your members in the list and iterate through the list .

Before Java provided the Iterator pattern , iterating through a collection was not generic:

For iterating through a List , you had to use index based access:

for(int i=0;i< yourlist.size();i++){
    System.out.println(yourlist.get(i));
}

For iterating through a Map, the above index based iteration won’t work since Map does not store data in sequence.

Similarly to iterate through a Tree , you can’t use index. You need to do a preorder, postorder or inorder traversal.

Different traversals for different collections.

Iterator pattern eliminates all of that.

It provides a generic interface for iterating through any collection.

All you have to do is get an iterator and start getting the elements. Something like this :

Iterator iterator = collection.iterator();

while(iterator.hasNext()){
  System.out.println(iterator.next());
}

Starting Java 1.2 , Iterators came to the picture.

Perhaps they read the Gang of Four book and implemented the pattern. And every collection in Java now lets you iterate them through a generic Iterator interface.

Coming back to your problem,

Let’s assume you are storing the fan member details in the list. To get all the male members , you can iterate through the list , make a conditional check – if gender is “male”, and then print the details.

Easy.

But ,

Let’s do this differently.

Let’s assume we are not going to use ‘List’ collection explicitly. We are going to use our own custom collection “FilmWatchersRepository“.

And we are going to implement our own Iterators.

One Iterator which iterates through only male members “MaleFansIterator“.

Another Iterator which iterates through only female members “FemaleFansIterator“.

And another Iterator which iterates through kids “KidFansIterator

Iteration tailored to our needs. No conditional checks required. Elegant.

Let’s get into action.

Here is the client code where the final action takes place:

package behavioural.iterator.pattern;

public class Client {

	public static void main(String a[]) {

		FilmFan fan1 = new FilmFan("Hrithik", "male", 35);
		FilmFan fan2 = new FilmFan("Gayathri", "female", 25);
		FilmFan fan3 = new FilmFan("Renuka", "female", 12);
		FilmFan fan4 = new FilmFan("Raghav", "male", 32);
		FilmFan fan5 = new FilmFan("Gopal", "male", 11);
		FilmFan fan6 = new FilmFan("Kannagi", "female", 28);
		FilmFan fan7 = new FilmFan("Kumar", "male", 45);
		FilmFan fan8 = new FilmFan("Loyala", "female", 23);
		FilmFan fan9 = new FilmFan("Agarnika", "female", 10);
		FilmFan fan10 = new FilmFan("Ramayan", "male", 28);

		FilmWatchersRepository repository = new FilmWatchersRepository();
		repository.add(fan1);
		repository.add(fan2);
		repository.add(fan3);
		repository.add(fan4);
		repository.add(fan5);
		repository.add(fan6);
		repository.add(fan7);
		repository.add(fan8);
		repository.add(fan9);
		repository.add(fan10);

		System.out.println("List of female fans");

		FansIterator femaleIterator = new FemaleFansIterator(repository);

		while (femaleIterator.hasNext()) {

			FilmFan fan = femaleIterator.next();

			System.out.println(fan);
		}

		System.out.println("List of male fans");

		FansIterator maleIterator = new MaleFansIterator(repository);

		while (maleIterator.hasNext()) {

			FilmFan fan = maleIterator.next();

			System.out.println(fan);
		}

		System.out.println("List of kids fans");

		FansIterator kidsIterator = new KidFansIterator(repository);

		while (kidsIterator.hasNext()) {

			FilmFan fan = kidsIterator.next();

			System.out.println(fan);
		}

	}
}

We are creating 10 film fans of different gender and age.

We are adding all those fans to our collection data structure “FilmWatchersRepository

And then we bring in three Iterators as mentioned earlier.

They iterate through the repository and print male , female and kid fans details respectively.

Looks elegant isn’t it. No conditional checks.

Let’s look at the Iterator interface which is implemented by all the Iterators:

package behavioural.iterator.pattern;

public interface FansIterator {
	
	
	boolean hasNext();
	
	FilmFan next();

}

It just has two methods , one to check if any more element exists in a collection during the traversal and another which fetches the next element in the collection.

Here is the implementation of the iterator for MaleFansIterator:

package behavioural.iterator.pattern;

import java.util.List;

public class MaleFansIterator implements FansIterator {
	

	FilmWatchersRepository repository;

	int current;
	
	public MaleFansIterator(FilmWatchersRepository repository) {

		this.repository = repository;

		this.current = 0;
	}


	@Override
	public boolean hasNext() {
		
		

		List<FilmFan> filmFans = this.repository.filmFanList;
		
		if(this.current == filmFans.size()) {
			
			return false;
		}
		
		FilmFan fan = filmFans.get(this.current);

		if (fan.getGender().equals("male")) {

			return true;
		} else {

			while (!fan.getGender().equals("male") && this.current < filmFans.size() - 1) {

				this.current++;

				fan = filmFans.get(this.current);
			}

			if (fan.getGender().equals("male")) {

				return true;
			}
		}

		return false;

	}

	@Override
	public FilmFan next() {

		FilmFan fan = this.repository.filmFanList.get(this.current);
		
		this.current++;

		return fan;

	}


}

As you see ,

The class has a reference to the repository which it gets through the constructor.

It has a variable current which keeps track of the current element in an iteration.

It has a hasNext() method (from the interface) which checks if there is any ‘male’ present in the collection starting from the current traversal index.

It has a next() method (from the interface ) which returns the element pointed to by the current traversal index.

Similar Iterator classes are created for Female and Kids (Anyone below the age 13 is considered a kid in the Iterator).

Below is the Fan class which we are storing in the repository:

package behavioural.iterator.pattern;

public class FilmFan {

	private String name;

	private String gender;

	private int age;

	public FilmFan(String name, String gender, int age) {

		this.name = name;

		this.gender = gender;

		this.age = age;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getGender() {
		return gender;
	}

	public void setGender(String gender) {
		this.gender = gender;
	}

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}

	@Override
	public String toString() {
		return "FilmFan [name=" + name + ", gender=" + gender + ", age=" + age + "]";
	}

}

And here is the output got on running the client code :

List of female fans
FilmFan [name=Gayathri, gender=female, age=25]
FilmFan [name=Renuka, gender=female, age=12]
FilmFan [name=Kannagi, gender=female, age=28]
FilmFan [name=Loyala, gender=female, age=23]
FilmFan [name=Agarnika, gender=female, age=10]
List of male fans
FilmFan [name=Hrithik, gender=male, age=35]
FilmFan [name=Raghav, gender=male, age=32]
FilmFan [name=Gopal, gender=male, age=11]
FilmFan [name=Kumar, gender=male, age=45]
FilmFan [name=Ramayan, gender=male, age=28]
List of kids fans
FilmFan [name=Renuka, gender=female, age=12]
FilmFan [name=Gopal, gender=male, age=11]
FilmFan [name=Agarnika, gender=female, age=10]

That’s it!.

We created our own collection data type.

We created our own iterators to iterate the data type.

Thanks to the Gang of Four.

Quoting them :

Provide a way to access the elements of an aggregate object sequentially without exposing its underlying representation

And they suggest to use the pattern to:

– access an aggregate object’s contents without exposing its internal representation (in our case our client code is unaware of the internal representation of our repository – we used lists internally to store the fan details)

– support multiple traversal of aggregate objects ( we iterated the repository in three ways – one for male, one for female and one for kids)

– provide a uniform interface for traversing different aggregate structures (in our case we traversed through a single repository through three different iterators. We could also create multiple repositories , bring them all under a single interface and use the same three iterators to iterate them)

Here is the code :

https://github.com/vijaysrj/designPatternsGoF/tree/master/src/behavioural/iterator/pattern


Posted

in

,

by

Comments

Leave a Reply

Discover more from The Full Stack Developer

Subscribe now to keep reading and get access to the full archive.

Continue reading