The Iterator Design Pattern is the third pattern in the Behavioral Category that I would like to talk about. Let's take a look at how and when to implement this pattern in C# and .Net.
Note: Code can be downloaded at my Github.
1. What is the Iterator Design Pattern?
Iterator is a behavioral design pattern that lets you traverse elements of a collection without exposing its underlying representation (list, stack, tree, etc.). The iterator pattern decouples algorithms from containers; in some cases, algorithms are necessarily container-specific and thus cannot be decoupled.
The iterator pattern is a design pattern in which an iterator is used to traverse a container and access the container's elements.
2. When to implement the Iterator Design Pattern?
The Iterator Design Pattern is used when:
- Your collection has a complex data structure under the hood, but you want to hide its complexity from clients (either for convenience or security reasons).
- You want to reduce duplication of the traversal code across your app.
- You want your code to be able to traverse different data structures or when types of these structures are unknown beforehand.
3. How to implement the Iterator Design Pattern?
Below are steps to implement the Iterator Design Pattern.
Step 1: Create an Iterator abstract class
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | abstract class Iterator : IEnumerator { object IEnumerator.Current => Current(); // Returns the key of the current element public abstract int Key(); // Returns the current element public abstract object Current(); // Move forward to next element public abstract bool MoveNext(); // Rewinds the Iterator to the first element public abstract void Reset(); } abstract class IteratorAggregate : IEnumerable { // Returns an Iterator or another IteratorAggregate for the implementing // object. public abstract IEnumerator GetEnumerator(); } |
Step 2: Create an Ordering class
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 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 | class AlphabeticalOrderIterator : Iterator { private WordsCollection _collection; // Stores the current traversal position. An iterator may have a lot of // other fields for storing iteration state, especially when it is // supposed to work with a particular kind of collection. private int _position = -1; private bool _reverse = false ; public AlphabeticalOrderIterator(WordsCollection collection, bool reverse = false ) { this ._collection = collection; this ._reverse = reverse; if (reverse) { this ._position = collection.getItems().Count; } } public override object Current() { return this ._collection.getItems()[_position]; } public override int Key() { return this ._position; } public override bool MoveNext() { int updatedPosition = this ._position + ( this ._reverse ? -1 : 1); if (updatedPosition >= 0 && updatedPosition < this ._collection.getItems().Count) { this ._position = updatedPosition; return true ; } else { return false ; } } public override void Reset() { this ._position = this ._reverse ? this ._collection.getItems().Count - 1 : 0; } } // Concrete Collections provide one or several methods for retrieving fresh // iterator instances, compatible with the collection class. class WordsCollection : IteratorAggregate { List< string > _collection = new List< string >(); bool _direction = false ; public void ReverseDirection() { _direction = !_direction; } public List< string > getItems() { return _collection; } public void AddItem( string item) { this ._collection.Add(item); } public override IEnumerator GetEnumerator() { return new AlphabeticalOrderIterator( this , _direction); } } |
Done! So now you can execute the code on the client:
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 | class Program { static void Main( string [] args) { // The client code may or may not know about the Concrete Iterator // or Collection classes, depending on the level of indirection you // want to keep in your program. var collection = new WordsCollection(); collection.AddItem( "First" ); collection.AddItem( "Second" ); collection.AddItem( "Third" ); Console.WriteLine( "Straight traversal:" ); foreach (var element in collection) { Console.WriteLine(element); } Console.WriteLine( "\nReverse traversal:" ); collection.ReverseDirection(); foreach (var element in collection) { Console.WriteLine(element); } } } |
Output:
4. Conclusion
The Iterator Design Pattern is needed when you expect to have a complicated collection and have to process the list by looping through it. I hope this tutorial is helpful. Please let me know your thoughts in the comment section below.