Iterator - Design Pattern

Iterator - Design Pattern

Software Architecture Simplified

ยท

5 min read

Objective ๐ŸŽฏ

Provide a way to access elements of a list sequentially without exposing its underlying representation.

Type โœ…

โœ”๏ธBehavioral: Describes how objects interact/communicate between themselves.

โŒCreational: Describes how to instantiate an object without large and complex.

โŒStructural: Describes how objects/classes are composed to form larger structures.

UML ๐Ÿ“

image.png

Participants ๐Ÿ”—

โ€ข Iterator:

  • Defines an interface to access elements in the collection

โ€ข ConcreteIterator:

  • Implements the Iterator interface
  • Keeps track of the current position in the traversal of the collection

โ€ข Aggregate:

  • Defines the interface of the Iterator object that will manipulate the collection

โ€ข ConcreteAggregate:

  • Responsible for maintaining the real list of objects that will be manipulated
  • Implements the Aggregate interface and its method to return a ConcreteIterator

Notes ๐Ÿ“

โ€ข In the ConcreteAggregate object, an Indexer must be implemented to avoid external manipulation to the list items.

Sample Code ๐ŸŽฎ

Structural Example ๐Ÿ›๏ธ

image.png

public static class IteratorStructural
    {
        public static void Execute()
        {
            ConcreteAggregate lConcreteAggregate = new ConcreteAggregate();
            lConcreteAggregate[0] = "Item A";
            lConcreteAggregate[1] = "Item B";
            lConcreteAggregate[2] = "Item C";
            lConcreteAggregate[3] = "Item D";

            // Create Iterator and provide aggregate
            ConcreteIterator lConcreteIterator = new ConcreteIterator(lConcreteAggregate);

            Console.WriteLine("Iterating over collection:");

            object lobject = lConcreteIterator.First();
            while (lobject != null)
            {
                Console.WriteLine(lobject);
                lobject = lConcreteIterator.Next();
            }
        }
    }

    public abstract class Aggregate
    {
        public abstract Iterator CreateIterator();
    }

    public class ConcreteAggregate : Aggregate
    {
        private ArrayList _Items = new ArrayList();

        public override Iterator CreateIterator()
        {
            return new ConcreteIterator(this);
        }

        public int Count
        {
            get { return _Items.Count; }
        }

        public object this[int prIndex]
        {
            get { return _Items[prIndex]; }
            set { _Items.Insert(prIndex, value); }
        }
    }

    public abstract class Iterator
    {
        public abstract object First();
        public abstract object Next();
        public abstract bool IsDone();
        public abstract object CurrentItem();
    }

    public class ConcreteIterator : Iterator
    {
        private ConcreteAggregate _ConcreteAggregate;
        private int _CurrentElement = 0;

        public ConcreteIterator(ConcreteAggregate prConcreteAggregate)
        {
            _ConcreteAggregate = prConcreteAggregate;
        }

        public override object First()
        {
            return _ConcreteAggregate[0];
        }

        public override object Next()
        {
            object lResult = null;
            if (_CurrentElement < _ConcreteAggregate.Count - 1)
            {
                lResult = _ConcreteAggregate[++_CurrentElement];
            }

            return lResult;
        }

        public override object CurrentItem()
        {
            return _ConcreteAggregate[_CurrentElement];
        }

        public override bool IsDone()
        {
            return _CurrentElement >= _ConcreteAggregate.Count;
        }
    }

Output

image.png

Real-world Example ๐Ÿ”ฅ

image.png

public static class IteratorPractical
    {
        public static void Execute()
        {
            OfficeFiles lOfficeFiles = new OfficeFiles();
            lOfficeFiles[0] = new File() { Name = "File001_PB", IsPrivate = false };
            lOfficeFiles[1] = new File() { Name = "File002_PV", IsPrivate = true };
            lOfficeFiles[2] = new File() { Name = "File003_PB", IsPrivate = false };
            lOfficeFiles[3] = new File() { Name = "File004_PV", IsPrivate = true };
            lOfficeFiles[4] = new File() { Name = "File005_PB", IsPrivate = false };
            lOfficeFiles[5] = new File() { Name = "File006_PV", IsPrivate = true };

            FileIterator lFileIterator = lOfficeFiles.CreateIterator();
            Console.WriteLine("Iterating over files using \"Public File Iterator\": ");

            File lFile = lFileIterator.First();
            Console.WriteLine("First File: " + lFile.Name);

            lFile = lFileIterator.Next();
            Console.WriteLine("Next File: " + lFile.Name);

            List<File> lFiles = lFileIterator.GetAll();
            Console.WriteLine("All Files: " + String.Join(", ", lFiles.Select(X => X.Name).ToList()));

            lFileIterator = new PrivateFileIterator(lOfficeFiles);
            Console.WriteLine("\n\rIterating over files using \"Private File Iterator\": ");

            lFile = lFileIterator.First();
            Console.WriteLine("First File: " + lFile.Name);

            lFile = lFileIterator.Next();
            Console.WriteLine("Next File: " + lFile.Name);

            lFiles = lFileIterator.GetAll();
            Console.WriteLine("All Files: " + String.Join(", ", lFiles.Select(X => X.Name).ToList()));
        }
    }

    // Aggregate (Concrete)
    public class OfficeFiles
    {
        private ArrayList _Items = new ArrayList();

        public FileIterator CreateIterator()
        {
            return new PublicFileIterator(this);
        }

        public int Count
        {
            get { return _Items.Count; }
        }

        public object this[int prIndex]
        {
            get { return _Items[prIndex]; }
            set { _Items.Insert(prIndex, value); }
        }
    }

    // Iterator
    public abstract class FileIterator
    {
        public abstract File First();
        public abstract File Next();
        public abstract List<File> GetAll();
        public abstract File CurrentItem();
    }

    // Concrete Iterator
    public class PublicFileIterator : FileIterator
    {
        private OfficeFiles _OfficeFiles;
        private int _CurrentElement = 0;

        public PublicFileIterator(OfficeFiles prOfficeFiles)
        {
            _OfficeFiles = prOfficeFiles;
            this.First(); // To set the correct _CurrentElement
        }

        public override File First()
        {
            for (int i = 0; i < _OfficeFiles.Count; i++)
            {
                if (((File)_OfficeFiles[i]).IsPrivate == false)
                {
                    _CurrentElement = i;
                    return ((File)_OfficeFiles[_CurrentElement]);
                }

            }
            return null;
        }

        public override File Next()
        {
            object lResult = null;

            for (int i = _CurrentElement; i < _OfficeFiles.Count; i++)
            {
                _CurrentElement++;
                if (((File)_OfficeFiles[_CurrentElement]).IsPrivate == false)
                    return ((File)_OfficeFiles[_CurrentElement]);
            }

            return null;
        }

        public override List<File> GetAll()
        {
            List<File> lResult = new List<File>();

            for (int i = 0; i < _OfficeFiles.Count; i++)
            {
                if (((File)_OfficeFiles[i]).IsPrivate == false)
                    lResult.Add(((File)_OfficeFiles[i]));
            }

            return lResult;
        }

        public override File CurrentItem()
        {
            return ((File)_OfficeFiles[_CurrentElement]);
        }
    }

    // Concrete Iterator
    public class PrivateFileIterator : FileIterator
    {
        private OfficeFiles _OfficeFiles;
        private int _CurrentElement = 0;

        public PrivateFileIterator(OfficeFiles prOfficeFiles)
        {
            _OfficeFiles = prOfficeFiles;
            _OfficeFiles = prOfficeFiles;
            this.First(); // To set the correct _CurrentElement
        }

        public override File First()
        {
            for (int i = 0; i < _OfficeFiles.Count; i++)
            {
                if (((File)_OfficeFiles[i]).IsPrivate == true)
                {
                    _CurrentElement = i;
                    return ((File)_OfficeFiles[_CurrentElement]);
                }

            }
            return null;
        }

        public override File Next()
        {
            object lResult = null;

            for (int i = _CurrentElement; i < _OfficeFiles.Count; i++)
            {
                _CurrentElement++;
                if (((File)_OfficeFiles[_CurrentElement]).IsPrivate == true)
                    return ((File)_OfficeFiles[_CurrentElement]);
            }

            return null;
        }

        public override List<File> GetAll()
        {
            List<File> lResult = new List<File>();

            for (int i = 0; i < _OfficeFiles.Count; i++)
            {
                if (((File)_OfficeFiles[i]).IsPrivate == true)
                    lResult.Add(((File)_OfficeFiles[i]));
            }

            return lResult;
        }

        public override File CurrentItem()
        {
            return ((File)_OfficeFiles[_CurrentElement]);
        }
    }

    public class File
    {
        public string Name { get; set; }
        public bool IsPrivate { get; set; }
    }

Output

image.png

Source Code ๐ŸŽฒ

github.com/VictorLins/DesignPatterns

Did you find this article valuable?

Support Victor Lins by becoming a sponsor. Any amount is appreciated!

ย