design pattern

Behavioral Design Patterns: Iterator

Today we’ll be exploring the Iterator design pattern in our ongoing Guide to Software Design Patterns series. The iterator design pattern may be the most frequently used pattern we’ll be looking at throughout this entire collection of articles. The purpose of this pattern is to traverse through a collection of objects and retrieve each element in turn. This practice is used in programming so often that many developers may not even realize it’s considered a design pattern at all!

In this article we’ll examine the iterator design pattern in more detail, looking at both a real world example and some fully-functional C# code that will illustrate a few different ways this pattern can be implemented. Let’s get going!

In the Real World

One of the fundamental features of the iterator pattern is that it should allow a client to iterate through a collection, without any knowledge about the implementation of that collection, or the specifics and ordering of individual elements. Thus, this sort of iteration through an anonymous collection reminds me a lot of a real-world audition process.

Imagine you’re the theater director of the popular local theater. A playwright comes to you looking to put on a production of her next big hit, Hamilton 2: Lincoln’s Revenge. You put the word out that there are half a dozen major parts available and auditions will be held the next day. Audition day rolls around and you sit third row center. After meticulously arranging your pen and notepad just so, you call out, with appropriately dramatic flourish, that you’re ready to begin. The first auditionee is a meek, mousy young woman who doesn’t seem to fit the part of the boisterous and fiendish queen she’s trying out for, so you jot down a note and quickly shout, “Thank you. Next!” Out comes the next lamb to the slaughter, and the process repeats, again and again.

This is the core of the iterator design pattern. There’s a group (collection) of performers waiting backstage, but you (as the client) know nothing of what that group is made up of, the order they are in, or the individual people (elements) within it. All you know is that when you yell, “Next!”, another person from the larger group comes on stage and gives it their all, and then awaits your judgment.

How It Works In Code

Since we’re using C# for our sample code, implementing our own iterator design pattern is extremely easy, if we want it to be. .NET provides built-in interfaces, like IEnumerable, that make traversal over a collection a piece of cake to code.

Therefore, in the spirit of actually illustrating the underlying components of a traditional iterator pattern, our code sample will contain two different types of iterator implementations. The basic iterator will show a more traditional pattern and all the actual components necessary to make a simple iterator in just about any programming language. The advanced iterator, on the other hand, illustrates how we can use built-in .NET interfaces and members to implement an iterator as quickly and easily as possible.

As usual, we begin with the full code sample below, so it can be copy/pasted for your own testing purpose if you’re following along. After the code break, we’ll then dive into the specifics and see exactly what’s going on:

For all generic implementations of the iterator design pattern we effectively need three components:

  • Aggregate – Houses the collection of elements to be iterated, along with a way to access an Iterator instance.
  • Iterator – Handles iteration logic of the passed Aggregate collection.
  • Client – Creates the Aggregate instance and collection, then performs the actual iteration looping.

Therefore, our basic iterator example begins with the IAggregate interface and Aggregate class that implements it:

The primary functionality is that the Aggregate should hold our object collection and provide an Iterator instance so we can iterate over the elements. For simplicity, we’ve also provide access to the underlying elements by index via the this[int index] property. This will be used in the Iterator class to perform iteration based on the element index.

Speaking of the iterator, let’s take a look at the IIterator interface and the Iterator class that implements it:

The fundamental requirements of the Iterator is that it can retrieve the next element in the underlying Aggregate collection. We just iterate the private _index property when the Next() method is called, returning a boolean that indicates if the index is within the bounds of the collection or not. Alternatively, we could also have the Next() method return the next object element, but we’ve abstracted that task to the Current property instead.

To test out our basic iterator we now need some sort of client code, which is setup in the main Program class:

The BasicIteratorTest() method performs all the client logic. In this case, we create a new Aggregate instance and populate its collection with a list of Books. Next, we need to retrieve an instance of the Iterator that will iterate over the aggregate collection. With the iterator in hand we can call iterator.Next() to see if the next element exists, and while it does so, call iterator.Current and output the result to the log.

If all is working correctly, we should get a log output for each Book. Sure enough, that’s just what we see:

That shows the generic way of implementing an iterator in most languages. Now, let’s look at the advanced iterator, where we use built-in components provided by .NET to simplify the process and limit our code requirement as much as possible.

Our iterator and aggregate components are all contained within the Iterator<T> class:

We want to ensure this iterator is generic (can be used with any type of object), so we use <T> throughout the code. Most importantly, the Iterator<T> class implements the IEnumerable<T> interface, which exposes built-in members that handle iteration logic for us. All we need to do is define the GetEnumerator() and IEnumerable.GetEnumerator() methods within our class, and .NET will handle the rest for us. In this case, GetEnumerator() loops through the collection via the index and returns each element via the yield keyword. The yield keyword halts execution each time it is called, allowing the iterator to call each subsequent element within the collection.

The Program.AdvancedIteratorTest() method acts as our client component:

We start by creating a new Iterator<Book> instance, then add the Book collection to the Values property. Now we can loop through the Iterator collection just as we would any other IEnumerable in C#. In this case, we can use foreach to retrieve each object, which is yielded to us via the GetEnumerator() method.

As before, we should see the output showing us all the Books in our collection:

There we have it! We saw two different methods for implementing the iterator design pattern, depending how much you want to rely on built-in APIs versus your own explicit code. For more information on all the other popular design patterns, head on over to our ongoing design pattern series here!