The Iterator pattern provides a way to access objects in an underlying representation without exposing access to the representation itself.
The idea is that we'll have a class, called an Iterator, which contains a reference to a corresponding aggregate object, and that Iterator can traverse over its aggregate to retrieve individual objects.
NOTE: This post is part of a series demonstrating software design patterns using C# and .NET. The patterns are taken from the book Design Patterns by the Gang of Four. Check out the other posts in this series!
The Rundown
- Type: Behavioral
- Useful? 5/5 (Extremely)
- Good For: Extracting objects from a collection without exposing the collection itself.
- Example Code: On GitHub
The Participants
- The Iterator defines an interface for accessing an Aggregate object and traversing elements within that Aggregate.
- The ConcreteIterator implements the Iterator interface and keeps track of its current position within the Aggregate.
- The Aggregate defines an interface for creating an Iterator object.
- The ConcreteAggregate implements the Iterator creation interface and returns a ConcreteIterator for that ConcreteAggregate.
A Delicious Example
To demo how we might use the Iterator design pattern, let's talk about my favorite sugary snack: jelly beans. As far as I am concerned, these little nuggets of sugar and flavor are the best thing since sliced bread.
We want to build a collection for a group of jelly beans and have that collection create an iterator for itself. To do this, let's first define a class JellyBean
to represent a single jelly bean.
/// <summary>
/// Our collection item. Mostly because I'm a sucker for jelly beans.
/// </summary>
class JellyBean
{
private string _flavor;
// Constructor
public JellyBean(string flavor)
{
this._flavor = flavor;
}
public string Flavor
{
get { return _flavor; }
}
}
Next we need an Aggregate participant, which we'll call ICandyCollection
, and our ConcreteAggregate participant, which we'll call JellyBeanCollection
. These classes represent a collection of jelly beans.
/// <summary>
/// The aggregate interface
/// </summary>
interface ICandyCollection
{
IJellyBeanIterator CreateIterator();
}
/// <summary>
/// The ConcreteAggregate class
/// </summary>
class JellyBeanCollection : ICandyCollection
{
private ArrayList _items = new ArrayList();
public JellyBeanIterator CreateIterator()
{
return new JellyBeanIterator(this);
}
// Gets jelly bean count
public int Count
{
get { return _items.Count; }
}
// Indexer
public object this[int index]
{
get { return _items[index]; }
set { _items.Add(value); }
}
}
Now we can define our Iterator and ConcreteIterator participants.
/// <summary>
/// The 'Iterator' interface
/// </summary>
interface IJellyBeanIterator
{
JellyBean First();
JellyBean Next();
bool IsDone { get; }
JellyBean CurrentBean { get; }
}
/// <summary>
/// The 'ConcreteIterator' class
/// </summary>
class JellyBeanIterator : IJellyBeanIterator
{
private JellyBeanCollection _jellyBeans;
private int _current = 0;
private int _step = 1;
// Constructor
public JellyBeanIterator(JellyBeanCollection beans)
{
this._jellyBeans = beans;
}
// Gets first jelly bean
public JellyBean First()
{
_current = 0;
return _jellyBeans[_current] as JellyBean;
}
// Gets next jelly bean
public JellyBean Next()
{
_current += _step;
if (!IsDone)
return _jellyBeans[_current] as JellyBean;
else
return null;
}
// Gets current iterator candy
public JellyBean CurrentBean
{
get { return _jellyBeans[_current] as JellyBean; }
}
// Gets whether iteration is complete
public bool IsDone
{
get { return _current >= _jellyBeans.Count; }
}
}
Notice that the ConcreteAggregate needs to implement methods by which we can manipulate objects within the collection, without exposing the collection itself. This is how it can fit with the Iterator design pattern.
Finally, in our Main()
method, we will create a collection of jelly beans and then iterate over them:
static void Main(string[] args)
{
// Build a collection of jelly beans
JellyBeanCollection collection = new JellyBeanCollection();
collection[0] = new JellyBean("Cherry");
collection[1] = new JellyBean("Bubble Gum");
collection[2] = new JellyBean("Root Beer");
collection[3] = new JellyBean("French Vanilla");
collection[4] = new JellyBean("Licorice");
collection[5] = new JellyBean("Buttered Popcorn");
collection[6] = new JellyBean("Juicy Pear");
collection[7] = new JellyBean("Cinnamon");
collection[8] = new JellyBean("Coconut");
// Create iterator
JellyBeanIterator iterator = collection.CreateIterator();
Console.WriteLine("Gimme all the jelly beans!");
for (JellyBean item = iterator.First();
!iterator.IsDone; item = iterator.Next())
{
Console.WriteLine(item.Flavor);
}
Console.ReadKey();
}
Will I Ever Use This Pattern?
Absolutely. The pattern is astonishingly useful when attempting to retrieve objects from collections that you'd rather not expose to outside usage (because that's, like, the pattern's entire purpose). If you primarily work in the ASP.NET world (as I do) and you use LINQ, you are already using the Iterator pattern extensively (e.g. collection.First()
or collection.Count()
).
Summary
The Iterator pattern provides a manner in which we can access and manipulate objects in a collection without exposing the collection itself. This pattern is incredibly common and incredibly useful, so keep it in mind; once you know what it is, you'll start seeing it everywhere.
Aaaaand now I need some sugar. The literal kind. Gimme all the jelly beans!
Now THAT'S what I'm talking about.
Happy Coding!