- It provides a way to access the elements of an aggregate object sequentially without exposing its underlying representation.
- A common programming task is to traverse and manipulate a collection of objects.
- These collections may be stored as an array, a list, or perhaps something more complex, such as a tree or graph structure.
- You may need access the items in the collection in a certain order, such as, front to back, back to front, depth first (as in tree searches), skip evenly numbered objects.
- The Iterator design pattern solves this problem by separating the collection of objects from the traversal of these objects by implementing a specialized iterator class.
Participants
Iterator
(AbstractIterator)
- Defines interface for accessing & traversing elements.
ConcreteIterator
(Iterator)
- Implements the Iterator interface.
- Keeps track of the current position in the traversal of the aggregate.
Aggregate
(AbstractCollection)
- Defines an interface for creating Iterator object.
ConcreteAggregate
(Collection)
- Implements the Iterator creation interface to return an instance of the proper
ConcreteIterator
.
var collection = new Collection();
collection[0] = new Item("Item 1");
collection[1] = new Item("Item 2");
collection[2] = new Item("Item 3");
collection[3] = new Item("Item 4");
collection[4] = new Item("Item 5");
var iterator = collection.CreateIterator();
iterator.Step = 2;
for (var item = iterator.First(); !iterator.IsDone; item = iterator.Next())
Console.WriteLine(item.Name);
public class Item
{
public string Name { get; set; }
public Item(string name) => Name = name;
}
public interface IAbstractCollection
{
Iterator CreateIterator();
}
public class Collection : IAbstractCollection
{
private readonly ArrayList _Items = new ArrayList();
public Iterator CreateIterator() => new Iterator(this);
public int Count => _Items.Count;
public object this[int index]
{
get => _Items[index];
set => _Items.Add(value);
}
}
public interface IAbstractIterator
{
Item First();
Item Next();
bool IsDone { get; }
Item CurrentItem { get; }
}
public class Iterator : IAbstractIterator
{
private Collection _Collection;
private int _Current = 0;
public Iterator(Collection collection) => _Collection = collection;
public Item First()
{
_Current = 0;
return _Collection[_Current] as Item;
}
public Item Next()
{
_Current += Step;
return IsDone ? null : _Collection[_Current] as Item;
}
public int Step { get; set; } = 1;
public Item CurrentItem => _Collection[_Current] as Item;
public bool IsDone => _Current >= _Collection.Count;
}
Rules of Thumb
- The abstract syntax tree of Interpreter is a Composite (therefore Iterator and Visitor are also applicable).
- Iterator can traverse a Composite.
- Visitor can apply an operation over a Composite.
- Polymorphic Iterators rely on Factory Methods to instantiate the appropriate Iterator subclass.
- Memento is often used in conjunction with Iterator.
- An Iterator can use a Memento to capture the state of an iteration.
- The Iterator stores the Memento internally.