© Vaskaran Sarcar 2020
V. SarcarDesign Patterns in C#https://doi.org/10.1007/978-1-4842-6062-3_18

18. Iterator Pattern

Vaskaran Sarcar1 
(1)
Garia, Kolkata, West Bengal, India
 

This chapter covers the Iterator pattern.

GoF Definition

Provide a way to access the elements of an aggregate object sequentially without exposing its underlying representation.

Concept

Iterators are generally used to traverse a container (or a collection of objects) to access its elements without knowing how the data are stored internally. It is very useful when you need to traverse different kinds of collection objects in a standard and uniform way. Figure 18-1 shows a sample and most common diagram for an Iterator pattern.
../images/463942_2_En_18_Chapter/463942_2_En_18_Fig1_HTML.jpg
Figure 18-1

A sample diagram for an Iterator pattern

The participants are described as follows.
  • Iterator is an interface that accesses or traverses elements.

  • ConcreteIterator implements the Iterator interface methods. It can also keep track of the current position in the traversal of the aggregate.

  • Aggregate defines an interface that can create an Iterator object.

  • ConcreteAggregate implements the Aggregate interface. It returns an instance of the ConcreteIterator .

Points to Note
  • It is frequently used to traverse the nodes of a tree-like structure. In many examples, you may notice the Iterator pattern with the Composite pattern.

  • The role of an iterator is not limited to traversing. This role can vary to support various requirements. For example, you can filter the elements in various ways.

  • Clients cannot see the actual traversal mechanism. A client program only uses public iterator methods.

  • The concept of iterators and enumerators has existed for a long time. Enumerators produce the next element based on a criterion, whereas using iterators, you cycle a sequence from a starting point to the endpoint.

  • It’s a common practice to apply a foreach iterator to a collection generated from an enumerator. You can then fetch the value and apply it in the body of the loop.

Real-World Example

Suppose there are two companies: Company A and Company B. Company A stores its employee records (i.e., each employee’s name, address, salary details, etc.) in a linked list data structure. Company B stores its employee data in an array. One day, the two companies decide to merge to form one big company. The Iterator pattern is handy in such a situation because you do need not to write the code from scratch. In a situation like this, you can have a common interface through which you can access the data for both companies. So, you can simply call those methods without rewriting the code.

Consider another example. Suppose your company has decided to promote some employees based on their performances. So, all the managers get together and set a common criterion for promotion. Then they iterate over the records of the employees one by one to mark the potential candidates for promotion.

You can consider the example from a different domain too. For example, when you store songs in your preferred audio devices (for example, into an MP3 player) or your mobile devices, you can iterate over them through various button press or swipe movements. The basic idea is to provide you a mechanism so that you can iterate over your list smoothly.

Computer-World Example

Go through the following two bullet points. These are common examples of Iterator pattern.
  • C# has iterators that were introduced in Visual Studio 2005. The foreach statement is frequently used in this context. To learn more about these built-in functionalities, refer to https://docs.microsoft.com/en-us/dotnet/csharp/iterators.

  • If you are familiar with Java, you may have used Java’s built-in Iterator interface, java.util.Iterator. This pattern is used in interfaces like java.util.Iterator or java.util.Enumeration.

Implementation

Similar to our real-world example, let’s assume that there is a college with two departments: the sciences and the arts. The arts department uses an array data structure to maintain its course details, but the science department is using a linked list data structure to keep the same. The administrative department does not interfere with how a department maintains these details. It is simply interested in getting the data from each department and wants to access the data uniformly. Now assume you are a member of the administrative department, and at the beginning of a new session, you want to advertise the curriculum using the iterators. Let’s see how we can implement it in the upcoming demonstration.

Let’s assume that you have an iterator called IIterator , which acts as the common interface in the upcoming example, and it currently supports four basic methods: First(), Next(), CurrentItem(), and IsCollectionEnds(), which are as follows.
  • The First() method reset the pointer to the first element before you start traversing a data structure.

  • The Next() method returns the next element in the container.

  • The CurrentItem() method returns the current element of the container that the iterator is pointing at a particular time.

  • The IsCollectionEnds() validates whether any next element is available for further processing or not. So, this method helps you to decide whether you have reached the end of your container.

These methods are implemented in each of the ScienceIterator and ArtsIterator classes. You’ll see that the CurrentItem() method is defined differently in the ScienceIterator and ArtIterator classes. Also, to print the curriculum, I used only two of these methods: IsCollectionEnds() and Next(). If you want, you can experiment with the two remaining methods, First() and currentItem(). I mentioned the four methods and provided some sample implementations for them because they are very common in Iterator pattern implementations. These sample implementations can help you understand those examples too.

Point to Note

The code size of the program can be halved if you consider either the sciences or the arts subjects only. But I kept them both to show you that the Iterator pattern can help you to traverse without knowing how the data are stored internally. For sciences, the subjects are stored in a linked list, but for arts, subjects are stored in an array. Still, by using this pattern, you can traverse and print the subjects in a uniform way.

Class Diagram

Figure 18-2 shows the class diagram.
../images/463942_2_En_18_Chapter/463942_2_En_18_Fig2_HTML.jpg
Figure 18-2

Class diagram

Solution Explorer View

Figure 18-3 shows the high-level structure of the program. It’s a big program and tough to accommodate everything properly in a single screenshot, so I expanded only the details for the Science department.
../images/463942_2_En_18_Chapter/463942_2_En_18_Fig3_HTML.jpg
Figure 18-3

Solution Explorer view

Demonstration 1

Here’s the implementation.
using System;
using System.Collections.Generic;
using System.Linq;
namespace IteratorPattern
{
    #region Iterator
    public interface IIterator
    {
        // Reset to first element
        void First();
        // Get next element
        string Next();
        // End of collection check
        bool IsCollectionEnds();
        // Retrieve Current Item
        string CurrentItem();
    }
    /// <summary>
    ///  ScienceIterator
    /// </summary>
    public class ScienceIterator : IIterator
    {
        private LinkedList<string> Subjects;
        private int position;
        public ScienceIterator(LinkedList<string> subjects)
        {
            this.Subjects = subjects;
            position = 0;
        }
        public void First()
        {
            position = 0;
        }
        public string Next()
        {
            return Subjects.ElementAt(position++);
        }
        public bool IsCollectionEnds()
        {
            if (position < Subjects.Count)
            {
                return false;
            }
            else
            {
                return true;
            }
        }
        public string CurrentItem()
        {
            return Subjects.ElementAt(position);
        }
    }
    /// <summary>
    ///  ArtsIterator
    /// </summary>
    public class ArtsIterator : IIterator
    {
        private string[] Subjects;
        private int position;
        public ArtsIterator(string[] subjects)
        {
            this.Subjects = subjects;
            position = 0;
        }
        public void First()
        {
            position = 0;
        }
        public string Next()
        {
            //Console.WriteLine("Currently pointing to the subject: "+ this.CurrentItem());
            return Subjects[position++];
        }
        public bool IsCollectionEnds()
        {
            if (position >= Subjects.Length)
            {
                return true;
            }
            else
            {
                return false;
            }
        }
        public string CurrentItem()
        {
            return Subjects[position];
        }
    }
    #endregion
    #region Aggregate
    public interface ISubjects
    {
        IIterator CreateIterator();
    }
    public class Science : ISubjects
    {
        private LinkedList<string> Subjects;
        public Science()
        {
            Subjects = new LinkedList<string>();
            Subjects.AddFirst("Mathematics");
            Subjects.AddFirst("Computer Science");
            Subjects.AddFirst("Physics");
            Subjects.AddFirst("Electronics");
        }
        public IIterator CreateIterator()
        {
            return new ScienceIterator(Subjects);
        }
    }
    public class Arts : ISubjects
    {
        private string[] Subjects;
        public Arts()
        {
            Subjects = new[] { "English", "History", "Geography", "Psychology" };
        }
        public IIterator CreateIterator()
        {
            return new ArtsIterator(Subjects);
        }
    }
    #endregion
    /// <summary>
    /// Client code
    /// </summary>
    class Client
    {
        static void Main(string[] args)
        {
            Console.WriteLine("***Iterator Pattern Demonstration.***");
            // For Science
            ISubjects subjects= new Science();
            IIterator iterator = subjects.CreateIterator();
            Console.WriteLine(" Science subjects :");
            Print(iterator);
            // For Arts
            subjects = new Arts();
            iterator = subjects.CreateIterator();
            Console.WriteLine(" Arts subjects :");
            Print(iterator);
            Console.ReadLine();
        }
        public static void Print(IIterator iterator)
        {
            while (!iterator.IsCollectionEnds())
            {
                Console.WriteLine(iterator.Next());
            }
        }
    }
}

Output

Here’s the output.
***Iterator Pattern Demonstration.***
Science subjects :
Electronics
Physics
Computer Science
Mathematics
Arts subjects :
English
History
Geography
Psychology
Note

You may use two or more different data structures in an implementation to demonstrate the power of this pattern. You have seen that in the previous demonstration, I used the First (), Next(), IsCollectionEnds(), and CurrentItem() methods with different implementations that vary due to their internal data structures.

One use of CurrentItem() is also shown in the commented code. If you want to test it, you can uncomment the line.

Demonstration 2

Now let’s look at another implementation using C#’s built-in support for iterator pattern. I used the IEnumerable interface , so you do not need to define a custom iterator. But to use this interface, you need to include the following line at the beginning of the program.
using System.Collections;
If you see the definition in Visual Studio, it describes the following.
//
// Summary:
//     Exposes an enumerator, which supports a simple iteration over a //     non-generic collection.
[NullableContextAttribute(1)]
public interface IEnumerable
{
 //
 // Summary:
 //     Returns an enumerator that iterates through a collection.
 //
 // Returns:
 //  An System.Collections.IEnumerator object that can be used to iterate
 //  through the collection.
 IEnumerator GetEnumerator();
 }
So, you can easily predict that each concrete iterator needs to implement the GetEnumerator() method . In the following implementation (demonstration 2), both concrete iterators define it as follows.
public IEnumerator GetEnumerator()
{
 foreach( string subject in Subjects)
  {
    yield return subject;
  }
}
You may wonder about the yield return. Microsoft discusses it at https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/yield.

When you use the yield contextual keyword in a statement, you indicate that the method, operator, or get accessor in which it appears is an iterator. Using yield to define an iterator removes the need for an explicit extra class (the class that holds the state for an enumeration, see IEnumerator<T> for an example) when you implement the IEnumerable and IEnumerator pattern for a custom collection type.

You use a yield return statement to return each element one at a time. The sequence returned from an iterator method can be consumed by using a foreach statement or LINQ query. Each iteration of the foreach loop calls the iterator method. When a yield return statement is reached in the iterator method, expression is returned, and the current location in code is retained. Execution is restarted from that location the next time that the iterator function is called.

These comments are self-explanatory. In short, the foreach of GetEnumerator can remember where it was after last yield return and can give you the next value. In the upcoming demonstration, the remaining code is easy to understand. Since the overall concept and intent are similar to demonstration 1, now you can directly jump to demonstration 2. Here’s the complete implementation.
using System;
using System.Collections;
using System.Collections.Generic;
namespace SimpleIterator
{
    public class Arts : IEnumerable
    {
        private string[] Subjects;
        public Arts()
        {
            Subjects = new[] { "English", "History", "Geography", "Psychology" };
        }
        public IEnumerator GetEnumerator()
        {
            foreach (string subject in Subjects)
            {
                yield return subject;
            }
        }
    }
    public class Science : IEnumerable
    {
        private LinkedList<string> Subjects;
        public Science()
        {
            Subjects = new LinkedList<string>();
            Subjects.AddFirst("Mathematics");
            Subjects.AddFirst("Computer Science");
            Subjects.AddFirst("Physics");
            Subjects.AddFirst("Electronics");
        }
        public IEnumerator GetEnumerator()
        {
            foreach (string subject in Subjects)
            {
                yield return subject;
            }
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("***Iterator Pattern.A simple demonstration using built-in constructs.***");
            Arts artsPapers = new Arts();
            Console.WriteLine(" Arts subjects are as follows:");
            /*
              Consume values from the
              collection's GetEnumerator()
             */
            foreach (string subject in artsPapers)
            {
                Console.WriteLine(subject);
            }
            Science sciencePapers = new Science();
            Console.WriteLine(" Science subjects are as follows:");
            /*
              Consume values from the
              collection's GetEnumerator()
             */
            foreach (string subject in sciencePapers)
            {
                Console.WriteLine(subject);
            }
        }
    }
}

Output

Here’s the output.
***Iterator Pattern.A simple demonstration using built-in constructs.***
Arts subjects are as follows:
English
History
Geography
Psychology
Science subjects are as follows:
Electronics
Physics
Computer Science
Mathematics

Q&A Session

18.1 What is the Iterator pattern used for?

The following discusses some of its usage.
  • You can traverse an object structure without knowing its internal details. As a result, if you have a collection of different subcollections (for example, your container is mixed with arrays, lists, linked lists, and so on), you can still traverse the overall collection and deal with the elements in a universal way without knowing the internal details or differences among them.

  • You can traverse a collection in different ways. If they are designed properly, multiple traversals are also possible in parallel.

18.2 What are the key challenges associated with this pattern?

You must make sure that no accidental modification has taken place during the traversal procedure.

18.3 But to deal with the challenge mentioned earlier, you can simply take a backup and then proceed. Am I right?

Taking a backup and re-examining it later is a costly operation.

18.4 In the code, I see a region named Aggregate. Is there any reason behind that naming?

An aggregate defines an interface to create an Iterator object . I adopted the name from the GoF book.

18.5 Throughout the discussion, you have talked about collections. What is a collection?

When you manage (or create) a related group of objects, in C#, you have the following choices.
  • You can consider arrays.

  • You can consider collections.

Collections are preferred in many cases because they can grow or shrink dynamically. In some collections, you can even assign keys to objects so that you can retrieve them at a later stage more efficiently with those keys. (For example, a dictionary is such a collection that is often used for fast lookups.) Lastly, a collection is a class, so before you add elements to it, you need to create instances. Here’s an example.
LinkedList<string> Subjects = new LinkedList<string>();
Subjects.AddLast("Maths");
Subjects.AddLast("Comp. Sc.");
Subjects.AddLast("Physics");

In this example, instead of AddFirst() method, I used the AddLast() method for a variation. Both methods are available and in-built in C#. The AddLast() method adds the node at the end of the LinkedList<T>, whereas the AddFirst() method adds the node at the beginning of LinkedList<T>.

18.6 In this implementation, you could simply consider using either of the science or arts subjects to demonstrate an implementation of an Iterator pattern and reduce the code size. Is this correct?

Yes, and I mentioned it before. But when you use two different data structures, you may visualize the real power of the Iterator design pattern. So, I kept them both here.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.17.79.60