Iterator





5.00/5 (1 vote)
Iterator
Imagine that you are a game developer. Your game is war strategy. Army has a complicated structure: it consists of Hero and three Groups. When King gives a decree to treat all soldiers (Hero is also soldier), you want to iterate through all soldiers and call treat()
method on each soldier instance. How can this be accomplished easily and without diving into Army
structure?
ITERATOR
Iterator is the pattern which provides you a way to access all elements in any collection of data without exposing collection implementation.
So in application to our problem. You just don't want to care about the structure of your army - you want some SoldiersIterator
to loop through all soldiers in it. Red line is the Iterator (in my mind representation).
Usage
The code below shows usage of iterator. We can loop through all soldiers and increase their health level without thinking of the structure of Army. This should be easy and it is the main purpose of Iterator
.
SoldiersIterator iterator = new SoldiersIterator(earthArmy);
while(iterator.hasNext()){
Soldier currSoldier = iterator.next();
currSoldier.treat();
}
Army Structure
Army structure has one Hero
and could have many Groups which of them could have many Soldiers. So as we see, structure of Army
is complicated and tree-based. The code below represents instantiating of Army
:
Army earthArmy = new Army();
Group groupA = new Group();
for(int i=1; i<4; ++i)
groupA.addNewSoldier(new Soldier("Alpha:" + i));
Group groupB = new Group();
for(int i=1; i<3; ++i)
groupB.addNewSoldier(new Soldier("Beta:" + i));
Group groupC = new Group();
for(int i=1; i<2; ++i)
groupC.addNewSoldier(new Soldier("Gamma:" + i));
earthArmy.ArmyHero = new Hero("Andriy Buday");
earthArmy.addArmyGroup(groupB);
earthArmy.addArmyGroup(groupA);
earthArmy.addArmyGroup(groupC);
Hero
is a class derived from Soldier
and has only one difference - higher health level:
public class Soldier {
public String Name;
public int Health;
protected int maxHealthPoints = 100;
public Soldier(String name){
Name = name;
}
public void treat(){
Health = maxHealthPoints;
System.out.println(Name);
}
}
public class Hero extends Soldier {
protected int maxHealthPoints = 500;
public Hero(String name) {
super(name);
}
}
SoldiersIterator
So if we can move through the complicated collection of Soldiers
, where is that complicity? Well, it is encapsulated in concrete Iterator
class.
public class SoldiersIterator {
private Army _army;
boolean heroIsIterated;
int currentGroup;
int currentGroupSoldier;
public SoldiersIterator(Army army) {
_army = army;
heroIsIterated = false;
currentGroup = 0;
currentGroupSoldier = 0;
}
public boolean hasNext() {
if(!heroIsIterated) return true;
if(currentGroup < _army.ArmyGroups.size())return true;
if(currentGroup == _army.ArmyGroups.size()-1)
if(currentGroupSoldier < _army.ArmyGroups.get(currentGroup).Soldiers.size())return true;
return false;
}
public Soldier next() {
Soldier nextSoldier;
// we still not iterated all soldiers in current group
if (currentGroup < _army.ArmyGroups.size()) {
if (currentGroupSoldier < _army.ArmyGroups.get(currentGroup).Soldiers.size()) {
nextSoldier = _army.ArmyGroups.get(currentGroup).Soldiers.get(currentGroupSoldier);
currentGroupSoldier++;
}
// moving to next group
else {
currentGroup++;
currentGroupSoldier = 0;
return next();
}
}
// hero is the last who left the battlefield
else if (!heroIsIterated) {
heroIsIterated = true;
return _army.ArmyHero;
} else {
// THROW EXCEPTION HERE
throw new IllegalStateException("End of colletion");
//or set all counters to 0 and start again, but not recommended
}
return nextSoldier;
}
}
Why My Example is Not Standard and Classic?
Because my goal in this article was to highlight the main issue that Iterator
solves in such a manner that it could be easily understandable. And the second reason is that you can read tonnes of standard explanations of this Design Pattern. The main difference between my explanation and others is that classic explanation is more abstracted, it contains abstract Iterator
which represents interface
for concrete ones, and abstract Aggregate
, which creates Iterators
needed.
The way I created Iterator
looked like:
SoldiersIterator iterator = new SoldiersIterator(earthArmy);
But generally this logic could be hidden under some Aggregate
's method (like GetEnumerator
in .NET). For example, I could have this:
AbstractIterator iterator = AbstractArmy.GetSoldiersIterator();// returns SoldiersIterator
In .NET world, we have IEnumerable<T>
and IEnumerator<T> interface
s which provides a lot of help in implementing this pattern:
var list = new List<int>();
//GetEnumerator is method of IEnumerator (Aggregate)
var enumerator = list.GetEnumerator();
//MoveNext method of IEnumerable (Iterator)
enumerator.MoveNext();
In Java, we have java.lang.Iterable
instead of IEnumerable
, which is more intuitive naming. I think that Microsoft just wanted to be original in this :).