The iterator pattern provides a client with a way to access the elements of an aggregate object sequentially without having to know the underlying representation. The iterator is especially useful because it provides the client with a common interface so it doesn’t need to know anything about the underlying data structure.
If you want to loop through an collection of elements, the iterator pattern comes in very handy. In this example the client first fills an aggregate object with string elements. Next, an iterator is instantiated with the collection. Last, the iteration is done. The fun part is that you can change the implementations of underlying data and iterator without having to change the client.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 |
Aggregate aggregate = new ConcreteAggregate(); for (int i = 0; i < 5; i++) { aggregate[i] = string.Format("listitem {0}", i); } Iterator iterator = new ConcreteIterator(aggregate); Console.WriteLine("Items in list:\n"); object item = iterator.First(); while (item != null) { Console.WriteLine(item); item = iterator.Next(); } |
The abstract aggregate class its most important abstract method is CreateIterator. For convenience of this example, an indexer and a property Count are also defined. The concrete aggregate object is just a wrapper around an ordinary ArrayList.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
abstract class Aggregate { public abstract object this[int i] { get; set; } public abstract int Count { get; } public abstract Iterator CreateIterator(); } class ConcreteAggregate : Aggregate { private ArrayList m_Items = new ArrayList(); public override Iterator CreateIterator() { return new ConcreteIterator(this); } public override int Count { get { return m_Items.Count; } } public override object this[int index] { get { return m_Items[index]; } set { m_Items.Insert(index, value); } } } |
The iterator does the looping work. First, an aggregate object is injected into the iterator upon instantiation. The rest of the code is just implementation of all abstract members.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
class ConcreteIterator : Iterator { private Aggregate m_Aggregate; private int m_Index = 0; public ConcreteIterator(Aggregate aggregate) { this.m_Aggregate = aggregate; } public override object First() { return m_Aggregate[0]; } public override object Next() { object result = null; if (m_Index < m_Aggregate.Count - 1) { result = m_Aggregate[++m_Index]; } return result; } public override object CurrentItem() { return m_Aggregate[m_Index]; } public override bool IsDone() { return m_Index >= m_Aggregate.Count; } } |
Knowing this, can you imagine a real world situation when to use this pattern? This is such an important pattern, that it is a built-in pattern in C#. If you want read more about use of this pattern, read Using the Iterator Pattern in C# ASP .NET, by Kory Becker
This post is part of a series on the foundational design patterns in C#. In this series we explore the ancient design patterns and their use in real-world programming situations of today. In these series of posts we explore each pattern by looking at a minimalistic example to reveal its structure and then look at more concrete and useful real-world C# code that implements the pattern.
Comments