Now I will discuss the Iterator pattern, as the name suggests, it is used to iterate through collections, objects, etc. This is, in a nutshell, more precisely described in the article.
Intent
- Providing sequential access to an object or objects composed of a specified number of elements without revealing the internal representation of these objects.
- Creation a unified interface for various aggregated (connected) structures is called polymorphic iteration support
Problem
Iterator pattern can be used eg in situations when you have iterated over various data structures of their elements, but it is illegible and the internal structure is revealed, then it is better to create one abstract interface for each structure which will define what it does and will be iterate over each structure in obvious way. And the client doesn’t need to know its exact implementation, it’s enough that this interface will only define what it does.
Use when:
- You want to have an interface that will allow you moving on the collections, collections or objects in your application in a uniform and understandable way.
- You want to access some collection or elements of some objects without revealing its exact implementation.
Discussion
The Iterator pattern has recently lost its significance when generic types appeared, although it also depends on the technology we use in the project, but in most cases there are many platforms that have generic types, the generic ones have an iteration method that iterates over the elements of the collection, e.g. lists in .NET . Nevertheless, it is worth knowing this pattern to know how it works from the inside and that we can create new interfaces adapted to our requirements, which iterates over data elements.
Take a look at the example below:
namespace Iterator
{
class Program
{
static void Main(string[] args)
{
var primes = new List<int> { 2, 3, 5, 7, 11, 13, 17, 19 };
foreach (var p in primes)
Console.Write(p + " ");
Console.ReadKey();
}
}
}
Lists have already implemented the Iterator pattern, we use ready-made solutions and the foreach loop displays the elements of the collection one by one.
The result is this:
Structure
In the Structure section we have a UML diagram, consisting of:
A client that uses the ListCollection and MapCollection classes that are inherits after the abstract class Collection, which defines the createTraversalObject() method, the createTraversalObject() method creates the ListTraversal class, and the ListTraversal class inherits from the TraversalAbstraction interface, which has defines iterate methods.
The pattern of the Iterator in the code looks something like this:
namespace IteratorSchema
{
class Program
{
public static void Main(string[] args)
{
ListCollection listCollection = new ListCollection();
for (TraversalAbstraction iter = listCollection.createIterator(); iter.hasNext();)
{
string name = (string)iter.next();
Console.WriteLine("Name : " + name);
}
Console.ReadKey();
}
}
public interface TraversalAbstraction
{
bool hasNext();
object next();
}
interface Collection
{
TraversalAbstraction createIterator();
}
class ListCollection: Collection
{
public TraversalAbstraction createIterator()
{
return new ListTraversal();
}
}
class ListTraversal : TraversalAbstraction
{
public string[] names = new string[] { "Robert", "John", "Julie", "Lora" };
int index;
public bool hasNext()
{
if (index < names.Length)
{
return true;
}
return false;
}
public Object next()
{
if (hasNext())
{
return names[index++];
}
return null;
}
}
}
This looks like similar as the UML diagram, we have defined the interface Collection, in which the createIterator method is defined (we called it differently than in the UML diagram createTraversalObject). We have a ListCollection class that implements the Collection interface and we use the method to create the ListTraversal class, the ListCollection class maybe remind you about implementation a other pattern if yes, you don’t mistake it. This is the factory method that is described halfway in the entry about the factory pattern.
The ListTraversal class implements methods which iterating after the collection are defined in the TraversalAbstraction interface. The hasNext() method checks whether the string on which we iterate, is not over, that we will not iterating through the empty collection, and the next() method checks with the hasNext() method that the collection is over, if not, we return another element from this collection.
The result is this:
List prototype from .NET
We will do more developed implementation of the Iterator pattern than the above, it will be such a prototype of list with .NET, because it will have some of its functionalities.
Let’s start with the class of saving values in the collection, in other words, aggregate.
namespace ListIterator
{
interface IAggregate
{
IIterator GetIterator();
string this[int itemIndex] { set; get; }
int Count { get; }
}
class MyAggregate : IAggregate
{
List<string> values_ = null;
public MyAggregate()
{
values_ = new List<string>();
}
public IIterator GetIterator()
{
return new MyIterator(this);
}
public string this[int itemIndex]
{
get
{
if (itemIndex < values_.Count)
{
return values_[itemIndex];
}
else
{
return string.Empty;
}
}
set
{
values_.Add(value);
}
}
public int Count
{
get
{
return values_.Count;
}
}
}
}
And the MyIterator class with implemented iteration fields after the collection. After the names you can guess what they are doing.
namespace ListIterator
{
interface IIterator
{
string FirstItem { get; }
string NextItem { get; }
string CurrentItem { get; }
bool IsDone { get; }
}
class MyIterator : IIterator
{
IAggregate aggregate_ = null;
int currentIndex_ = 0;
public MyIterator(IAggregate aggregate)
{
aggregate_ = aggregate;
}
public string FirstItem
{
get
{
currentIndex_ = 0;
return aggregate_[currentIndex_];
}
}
public string NextItem
{
get
{
currentIndex_ += 1;
if (IsDone == false)
{
return aggregate_[currentIndex_];
}
else
{
return string.Empty;
}
}
}
public string CurrentItem
{
get
{
return aggregate_[currentIndex_];
}
}
public bool IsDone
{
get
{
if (currentIndex_ < aggregate_.Count)
{
return false;
}
return true;
}
}
}
}
Calling these classes in the client looks like this:
namespace ListIterator
{
class Program
{
static void Main(string[] args)
{
MyAggregate aggr = new MyAggregate();
aggr[0] = "3";
aggr[1] = "5";
aggr[2] = "8";
aggr[3] = "9";
aggr[4] = "13";
aggr[5] = "34";
aggr[6] = "56";
aggr[7] = "45";
aggr[8] = "67";
aggr[9] = "15";
IIterator iter = aggr.GetIterator();
for (string s = iter.FirstItem; iter.IsDone == false; s = iter.NextItem)
{
Console.Write(s+" ");
}
Console.Write("\nElements quantities in colection: " + aggr.Count);
Console.ReadKey();
}
}
}
First, we save the data to the MyAggregate class, then call the GetIterator() method to create an instance of the MyIterator class so that we can operate on the MyAggregate class data, we operate on data in a for loop, in which we check the condition of terminating the loop with the field IsDone, element on which we operate in the loop whether is the last one in the collection if so then we finish the loop. And finally, we check how many elements are in the collection. Analyze this example calmly.
Result:
An example from life taken
Switching channels on the TV
For example, switching channels on the TV to the next or previous one.
In the code, we will do it as shown in the above figure. The code will look very much like the previous example.
The ChannelFrequencies interface as well as the TunedChannel class have the same logic as in the previous example of the IAggregate and MyAggregate classes, only the names of classes, methods and variables are different.
namespace IterateChannels
{
interface ChannelFrequencies
{
IIterator GetChannelIterator();
string this[int itemIndex] { set; get; }
int Count { get; }
}
class TunedChannel : ChannelFrequencies
{
List<string> values_ = null;
public TunedChannel()
{
values_ = new List<string>();
}
public IIterator GetChannelIterator()
{
return new Channelterator(this);
}
public string this[int itemIndex]
{
get
{
if (itemIndex < values_.Count)
{
return values_[itemIndex];
}
else
{
return string.Empty;
}
}
set
{
values_.Add(value);
}
}
public int Count
{
get
{
return values_.Count;
}
}
}
}
The ChannelIterator class also has almost the same logic as the previous example MyIterator class, I just only added the Previos field to it.
namespace IterateChannels
{
interface IIterator
{
string FirstChannel { get; }
string Next { get; }
string CurrentChannel { get; }
bool IsDone { get; }
string Previous { get; }
}
class Channelterator : IIterator
{
ChannelFrequencies aggregate_ = null;
int currentIndex_ = 0;
public Channelterator(ChannelFrequencies aggregate)
{
aggregate_ = aggregate;
}
public string FirstChannel
{
get
{
currentIndex_ = 0;
return aggregate_[currentIndex_];
}
}
public string Next
{
get
{
currentIndex_ += 1;
if (IsDone == false)
{
return aggregate_[currentIndex_];
}
else
{
return string.Empty;
}
}
}
public string Previous
{
get
{
currentIndex_ -= 1;
if (IsDone == false)
{
return aggregate_[currentIndex_];
}
else
{
return string.Empty;
}
}
}
public string CurrentChannel
{
get
{
return aggregate_[currentIndex_];
}
}
public bool IsDone
{
get
{
if (currentIndex_ < aggregate_.Count)
{
return false;
}
return true;
}
}
}
}
Only the customer is subject to quite a big change.
namespace IterateChannels
{
class Program
{
static void Main(string[] args)
{
TunedChannel aggr = new TunedChannel();
aggr[0] = "Channel 1";
aggr[1] = "Channel 2";
aggr[2] = "TVN";
aggr[3] = "PLUS";
aggr[4] = "POLSAT";
IIterator iter = aggr.GetChannelIterator();
string s = iter.FirstChannel;
string ToShiftChannel = null;
Console.WriteLine("We are starting from "+s);
while (!string.IsNullOrEmpty(ToShiftChannel = Console.ReadLine()) && iter.IsDone == false)
{
if (ToShiftChannel == "Next")
{
s = iter.Next;
Console.WriteLine(s);
}
else if(ToShiftChannel == "Previous")
{
s = iter.Previous;
Console.WriteLine(s);
}
}
Console.ReadKey();
}
}
}
In the while loop, we check what the client has chosen, depending on the option selected, we move one space forward in the channel collection or back.
Result:
Summary
That’s all about on subject the Iterator pattern🙂
Link to github with all examples: https://github.com/Slaw145/IteratorTutorial
This content also you can find on my blog http://devman.pl/programtech/design-patterns-iterator/
If you recognise it as useful, share it with others so that others can also use it.
Leave upvote and follow and wait for next articles :) .
In the next article, we will talk about the Chain of Responsibility design pattern.
Link to the site and group on fb:
– site on fb: Devman.pl-Sławomir Kowalski
– group on fb: DevmanCommunity
Ask, comment underneath at the end of the post, share it, rate it, whatever you want🙂. See you in a week, or rather in two weeks because I just don’t have a time make, I want have quality on this blog, not quantity, we’ll see how much time I will be have.🙂