Click here to Skip to main content
15,884,177 members
Articles / Operating Systems / Windows

Iterator Pattern

Rate me:
Please Sign up or sign in to vote.
3.08/5 (24 votes)
6 Sep 2005CPOL5 min read 50.2K   13   3
Iterator pattern well described

Introduction

GoF Definition

Provides a way to sequentially access elements of collection without exposing its underlying representation.

Let's Understand

  1. Sequential access elements.
    Think about this. Every collection could have different logic for storing elements and even different logic to determine sequence.
  2. Of collection.
    What we are iterating through is collection. This could be any collection. All we care about is it is a collection that should allow us to iterate in common way. Thinking about interface implementation? You are right.
  3. Without exposing its underlying representation.
    Collection should give something which will allow us to iterate through – also follows standard. Here we are talking about some separate object which implements concrete implementation. Yes, follow OO standard, create a separate class for this, which is specialized from some generalized mechanism.

Now let's see how many different types we talked about so far. First things first, we need collection(s) or aggregate(s).

So my first class in model: ConcreteAggregate.

This aggregate must have some common standard way to return iterating mechanism. When we talk about a standard without common implementation, we would select interface.

So my interface: Aggregate.

Guys, we are talking about some common mechanism all the time. What is this? Let us give some name. For a particular aggregate, I will have a particular mechanism, which allows to iterate through.

Let me call the concrete implementation class: ConcreteIterator.

Should the concrete iterator follow some standard so that we can access it through standard methods and the client will never come to know who did the job. Again standard! Again interface.

Interface: Iterator.

UML Diagram

Let us put together all of the above. I have some concrete aggregates that implement some common aggregate interface:

I should have a concrete iterator that implements standard iterator interface.

My concrete aggregate should return me a concrete iterator through aggregate interface implementation.

Finally, the client should have access to the following:

  1. Concrete aggregate
  2. Aggregate interface
  3. Iterator interface

Hey! This is not fair. The client is aware of everyone else but concrete iterator. Buddy, that’s what we want. Do you remember the phrase “without exposing its underlying representation”? So let's prepare our complete diagram:

Sample screenshot

Forward to Method Definitions

Let's go through sequence:

  1. Client will cast the concrete aggregate-to-aggregate interface to use standard method(s).
  2. Client will define variable of interface iterator type and set it through some method of aggregate interface. I got a method of aggregate interface: GetIterator() which returns iterator type.
  3. Client will write while loop to iterate. The condition to loop is that the iterator should have next sequence object available. So iterator should have a method called HasNext() which returns Boolean.
  4. If next object exist, client needs to get next sequence object as well move iterator pointer to its next at once. Client will call some method named: MoveNext() which moves sequence of iterator plus one and return next sequent object.
  5. Use client object.

Finally, I have classes defined with methods as below:

Rules to Follow

  1. Aggregate interface must have a method that returns iterator interface. This could be named anything. Commonly used names are CreateIterator() or GetIterator().
  2. Iterator interface must have a method to go to the next sequence. Typical names are Next() or MoveNext(). This method must return a sequential object and adds sequence with 1.
  3. Iterator should keep track of position.
  4. Concrete iterator must take concrete aggregate’s storing way as an argument in the constructor. Storing way could be array arraylist or any complex logic that only the concrete iterator and concrete aggregate may understand.
  5. Concrete iterator’s constructor should only be accessible from concrete aggregate(s). Make the constructor as project level – “internal” in C# and “Friend” in VB.NET.

Any Standard Examples in the Market?

Of course, yes. Let us understand what C# or VB.NET does with collections:

.NET framework has aggregate interface called IEnumerable and has iterator interface called IEnumerator. There are some concrete aggregates like Arraylist and Hashtable. Let us take Arraylist as our example of concrete aggregate. It implements Ienumberable. Ienumberable interface has a method named GetEnumerator(). This was our rule #1.

Arraylist has a class named ArraylistEnumeratorSimple which functions as a concrete iterator for us. ArraylistEnumeratorSimple class implements IEnumerator. IEnumerator has methods named MoveNext() and Current. .NET Framework has played a little trick here. MoveNext() method returns Boolean and Current provides current object. By doing this, .NET provides extra facility to get the same sequence object again if you want. (Do I really want the same object again? Never, as I have already set to some variable in client code so why should I use IEnumerator’s current and why not client’s local variable. .NET knows! Anyway, it's better so doesn’t matter. It followed our rule #2 and #3. Rule #3? Internally it keeps track of position which is private with variable name “index”.

ArraylistEnumeratorSimple is declared as inner class of ArrayList and its constructor is internal and takes arraylist itself as parameter. This follows rule #4 and #5.

The only words I may expect now: “What is the use of all these? I never cast my ArrayList as Ienumerable, never call GetIterator() method. This is all junk!” Take a chill-pill. Let us write a simple call iterating through ArrayList in C# or VB.NET:

C#
ArrayList al = new ArrayList();
Foreach(object o in object)
{
    //use o
}

Let us see IL for this(simplified):

IL_0008:  ldloc.0
IL_0009:  callvirt   instance class [mscorlib]
System.Collections.IEnumerator [mscorlib]
System.Collections.ArrayList::GetEnumerator()
IL_000e:  stloc.2
IL_000f:  br.s       IL_001e
IL_0011:  ldloc.2
IL_0012:  callvirt   instance object[mscorlib]
System.Collections.IEnumerator::get_Current()
IL_0017:  call       object [mscorlib]
System.Runtime.CompilerServices.RuntimeHelpers::GetObjectValue(object)
IL_001c:  stloc.1
IL_001d:  nop
IL_001e:  ldloc.2
IL_001f:  callvirt   instance bool [mscorlib]
System.Collections.IEnumerator::MoveNext()
IL_0024:  brtrue.s   IL_0011
IL_0026:  leave.s    IL_003f

Have fun! C# and VB.NET provide little additional facility to developers by giving keyword “for each” which internally writes the same code.

Where to Use in my Day-to-Day Programming?

  1. Do you use strongly typed collections in your application? Use this pattern. See the sample code in next section. A typical developer has a confusion whether to subclass Arraylist or Hashtable or to provide containment. If they provide containment, then it won’t allow you to use for each. Try it! Let me remind you, it has to cast to IEnumerable in for each loop.
  2. You can have one concrete iterator for similar type of collections. For example, if you are using Arraylist as your collection mechanism internally and don’t have any specific logic for sequence, then the same concrete iterator will work for those collections. For example, in the below mentioned example, there is a concrete iterator BooksEnumerator, rather name it as CommonArraylistEnumerator and use it from Books, Computers, and Racks … all collections which internally use ArrayList.

Sample Code

VB.NET
Public Class Book

    Private mName As String

    Public Sub New(ByVal bookName As String)
        mName = bookName
    End Sub 

    Public Property Name() As String
        Get 
            Return mName
        End Get 
        Set(ByVal Value As String)
            mName = Value
        End Set 
    End Property 

End Class 

Public Class Books
    Implements IEnumerable

    Private mList As New ArrayList

    Public Sub Add(ByVal bk As Book)
        mList.Add(bk)
    End Sub 

    Default Public ReadOnly Property List(ByVal index As Integer) As Book
        Get 
            Return DirectCast(mList(index), Book)
        End Get 
    End Property 

    Public Function GetEnumerator() As System.Collections.IEnumerator _
    	Implements System.Collections.IEnumerable.GetEnumerator
        Return New BooksEnumerator(mList)
    End Function 

    Class BooksEnumerator
        Implements IEnumerator

        Private index As Integer = -1
        Private mList As ArrayList

        Friend Sub New(ByVal list As ArrayList)
            mList = list
        End Sub 

        Public ReadOnly Property Current() As Object _
        	Implements System.Collections.IEnumerator.Current
            Get 
                Return mList(index)
            End Get 
        End Property 

        Public Function MoveNext() As Boolean _
        	Implements System.Collections.IEnumerator.MoveNext
            If index < mList.Count - 1 Then
                index += 1
                Return True 
            Else 
                Return False 
            End If 
        End Function 

        Public Sub Reset() Implements System.Collections.IEnumerator.Reset
            index = 0
        End Sub 
    End Class 
End Class 

History

  • 6th September, 2005: Initial post

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Web Developer
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
GeneralMy vote of 5 Pin
Oron Mizrachi3-Jun-13 9:55
Oron Mizrachi3-Jun-13 9:55 
GeneralMy favorite questions Pin
Thanh Dao6-Sep-05 21:14
Thanh Dao6-Sep-05 21:14 
This article is helpful and thanks for sharing. Just need a bit of work on reformating the code sample.

When dealing with design pattern, I sometime ask myself:

Are we trying to decouple things?

-> Iterator – do we want to separate the collection from the client that is using it so we don’t have to worry about having the right collection implementation?


Thanh Dao



-- modified at 3:19 Wednesday 7th September, 2005
GeneralRe: My favorite questions Pin
rajeevmehta27-Sep-05 3:30
rajeevmehta27-Sep-05 3:30 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.