What is the Iterator Design Pattern?

Göksu Deniz
6 min readDec 21, 2022

--

Image is created by DALL-E 2 AI

The Iterator design pattern is a behavioral design pattern that allows clients to access the elements of an aggregate object sequentially without exposing the underlying representation. It is often used to provide a standard way to traverse the elements of a collection, such as a list or an array.

Imagine that you have a collection of toys, and you want to be able to go through all of the toys one by one. You could use the Iterator design pattern to help you do this.

The Iterator design pattern is like a special helper that you can use to go through a collection of things, like toys, in a specific order. It knows how to start at the beginning of the collection and then move to the next item each time you ask it to.

To use the Iterator design pattern, you first need to have a collection of things. Then, you can ask the Iterator to give you the first item in the collection. After that, you can ask the Iterator to give you the next item, and it will move to the next item in the collection for you. You can keep asking for the next item until the Iterator runs out of items to give you.

The Iterator design pattern is a helpful way to go through a collection of things in an organized way. It can be especially useful if you have a lot of things in your collection and you want to make sure you don’t forget any of them.

In C#, the Iterator design pattern is implemented using the IEnumerable<T> and IEnumerator<T> interfaces, which are provided by the .NET Framework.

Here is an example of how you might implement an iterator in C#:

public class MyCollection<T> : IEnumerable<T>
{
private List<T> _items = new List<T>();

public void Add(T item)
{
_items.Add(item);
}

public IEnumerator<T> GetEnumerator()
{
return new MyEnumerator<T>(_items);
}

IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}

public class MyEnumerator<T> : IEnumerator<T>
{
private List<T> _items;
private int _index;

public MyEnumerator(List<T> items)
{
_items = items;
_index = -1;
}

public T Current
{
get
{
return _items[_index];
}
}

object IEnumerator.Current
{
get
{
return Current;
}
}

public void Dispose()
{
// Dispose of any resources used by the enumerator
}

public bool MoveNext()
{
_index++;
return _index < _items.Count;
}

public void Reset()
{
_index = -1;
}
}

To use the iterator, you can then do something like this:

var collection = new MyCollection<int>();
collection.Add(1);
collection.Add(2);
collection.Add(3);

foreach (int item in collection)
{
Console.WriteLine(item);
}

This will output “1", “2", and “3" to the console.

To use the iterator, you can use a foreach loop to iterate over the elements in the collection. The foreach loop will call the GetEnumerator method on the collection, and then it will repeatedly call the MoveNext method and access the Current property of the enumerator until the MoveNext method returns false, indicating that there are no more elements in the collection.

A list or enumerable collection is a data structure that stores a group of elements and provides ways to access and manipulate those elements. The Iterator design pattern is a way to access the elements of a collection in a specific way, using an iterator object that implements the IEnumerator<T> interface.

Imagine that you are building a web application that allows users to browse a catalog of products. The catalog has a large number of products, and you want to provide a way for users to filter and sort the products based on different criteria.

One way to implement this feature is to use the Iterator design pattern to create an iterator that allows users to access the products in the catalog in a specific order.

Here is an example of how you might implement the iterator in C#:

public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
}

public class ProductCatalog
{
private List<Product> _products = new List<Product>();

public void AddProduct(Product product)
{
_products.Add(product);
}

public ProductIterator CreateIterator(Func<Product, bool> filter, Func<Product, object> sort)
{
return new ProductIterator(_products, filter, sort);
}
}

public class ProductIterator : IEnumerator<Product>
{
private List<Product> _products;
private Func<Product, bool> _filter;
private Func<Product, object> _sort;
private int _index;

public ProductIterator(List<Product> products, Func<Product, bool> filter, Func<Product, object> sort)
{
_products = products;
_filter = filter;
_sort = sort;
_index = -1;
}

public Product Current
{
get
{
return _products[_index];
}
}

object IEnumerator.Current
{
get
{
return Current;
}
}

public void Dispose()
{
// Dispose of any resources used by the iterator
}

public bool MoveNext()
{
_index++;
while (_index < _products.Count)
{
if (_filter(_products[_index]))
{
return true;
}
_index++;
}
return false;
}

public void Reset()
{
_index = -1;
}
}

In this example, the ProductCatalog class represents the aggregate object, and the ProductIterator class represents the iterator. The ProductIterator class has a reference to the list of products, as well as a filter function and a sort function that are used to determine the order in which the products are returned.

To use the iterator, you can do something like this:

var catalog = new ProductCatalog();
catalog.AddProduct(new Product { Id = 1, Name = "Product 1", Price = 10 });
catalog.AddProduct(new Product { Id = 2, Name = "Product 2", Price = 20 });
catalog.AddProduct(new Product { Id = 3, Name = "Product 3", Price = 30 });

var iterator = catalog.CreateIterator(product => product.Price > 15, product => product.Name);

foreach (var product in iterator)
{
Console.WriteLine(product.Name);
}

The foreach loop will call the GetEnumerator method of the ProductIterator class, and then it will repeatedly call the MoveNext method and access the Current property of the iterator until the MoveNext method returns false, indicating that there are no more elements in the collection.

The output of this code will be:

// Product 2
// Product 3

UML Diagram

Created on stackedit.io

In this diagram, the Iterator class defines an interface for iterating over a collection of elements, and the ConcreteIterator class implements this interface for a specific Collection class. The Collection class defines an interface for creating an iterator, and the ConcreteCollection class implements this interface and provides a concrete implementation of the iterator. The Element class represents the elements in the collection.

Conclusion

In conclusion, the Iterator design pattern is a behavioral design pattern that allows clients to access the elements of an aggregate object sequentially without exposing the underlying representation. It is often used to provide a standard way to traverse the elements of a collection, such as a list or an array.

The Iterator design pattern is needed because it provides a standard way to access the elements of a collection in a specific order. This can be useful in a variety of scenarios, such as when you want to:

  1. Iterating over a large collection: If you have a large collection of elements, such as a list with millions of items, it might be inefficient to store all of the elements in memory at once. In this case, you can use the Iterator design pattern to implement lazy evaluation, where the elements of the collection are only generated when they are needed. This can be more efficient, as it allows you to avoid loading all of the elements into memory upfront.
  2. Iterating over a collection with complex logic: If you have a collection of elements that requires complex logic to access or manipulate the elements, you can use the Iterator design pattern to encapsulate this logic in the iterator. This can make your code easier to read and understand, as the logic for accessing the elements is isolated in a single place.
  3. Iterating over a collection with multiple traversals: If you need to perform multiple operations on the elements of a collection, you can use the Iterator design pattern to support multiple traversals. The iterator provides a standard way to reset the enumerator to its initial state, allowing you to traverse the collection multiple times.
  4. Hiding the implementation of a collection: If you want to hide the implementation details of a collection from the client, you can use the Iterator design pattern to provide a standard way to access the elements of the collection. This allows you to change the implementation of the collection without affecting the clients that are using it.

Thanks for reading! If you found the article helpful, you can clap and follow. So you will notified of new articles.

# Reference

It was created with the help of ChatGPT AI.

--

--

Göksu Deniz

Software Engineer, passionate about creating efficient solutions. Skilled in mentoring teams to deliver successful projects. Always exploring new tech trends.