Learning notes on design patterns: (15) iterator pattern

Posted by adityamenon90 on Thu, 20 Feb 2020 05:01:57 +0100

This note is excerpted from: https://www.cnblogs.com/PatrickLiu/p/7903617.html Record the learning process for future reference.

I. Introduction

Today we are going to talk about the third pattern of behavioral design pattern -- iterator pattern. Let's start with the name. Iteration means traversal. Iterators can be understood as traversal tools. What is traversal? In soft

In component design, of course, the iterator pattern is a general algorithm for traversing set objects. If there is only one type of collection, this pattern is useless because the collection object contains

Array, list, dictionary and hash table. It is not realistic to implement a set of traversal algorithm for each set object. Therefore, in order to solve the problem that there is a unified interface for traversing a set, so

We propose the "iterator" model.

II. Introduction to iterator mode

Iterator Pattern: English name -- Iterator Pattern; classification -- behavior type.

2.1. Motivation

In the process of software construction, the internal structure of collection objects often varies. But for these collection objects, we want to make the external client code access the package transparently without exposing its internal structure

At the same time, this "transparent traversal" also provides the possibility of "the same algorithm operates on a variety of collection objects".

Using object-oriented technology to abstract this traversal mechanism as "iterator object" provides an elegant way to "deal with the changing set object".

2.2 intention

Provides a way to access elements of an aggregate object in sequence without exposing its internal representation. ——Design mode GoF

2.3 structure diagram

2.4 composition of mode

As can be seen from the structure diagram of the iterator pattern, it involves four roles, namely:

1) Abstract iterator: Abstract iterator defines the interface for accessing and traversing elements, and generally declares the following methods: first() for obtaining the first element, next() for accessing the next element, and

Determine whether there is a hasNext() for the next element, a currentItem() for getting the current element, and implement these methods in its subclass.

2) concrete iterator: the concrete iterator implements the abstract iterator interface, traverses the collection object, and tracks its current position when traversing the aggregation.

3) Abstract aggregate class: Abstract aggregate class is used to store objects, define the interface to create corresponding iterator objects, and declare a createIterator() method to create an iterator object.

4) concrete aggregate: the concrete aggregate class implements the interface for creating corresponding iterators, implements the createIterator() method declared in the abstract aggregate class, and returns a concrete aggregate

Concrete iterator instance corresponding to coincidence.

2.5 implementation of iterator pattern

In real life, there are similar examples of iterator mode. For example, in the army, we can let someone in a certain team list, or let everyone in the queue sign up in turn. In fact, this process is

A process of traversing. The specific implementation code is as follows:

    class Program
    {
        /// <summary>
        /// Abstract aggregation class of force queue--This type is equivalent to an abstract aggregate class Aggregate
        /// </summary>
        public interface ITroopQueue
        {
            IIterator GetIterator();
        }

        /// <summary>
        /// Iterator abstract class
        /// </summary>
        public interface IIterator
        {
            bool MoveNext();
            object GetCurrent();
            void Next();
            void Reset();
        }

        /// <summary>
        /// Force queue specific aggregation class--Equivalent to concrete aggregate class ConcreteAggregate
        /// </summary>
        public sealed class ConcreteTroopQueue : ITroopQueue
        {
            private string[] collection;

            public ConcreteTroopQueue()
            {
                collection = new string[] { "Huang Feihong", "Fang Shi Yu", "Hong Xi Guan", "Yan Yong Chun" };
            }

            public IIterator GetIterator()
            {
                return new ConcreteIterator(this);
            }

            public int Length
            {
                get { return collection.Length; }
            }

            public string GetElement(int index)
            {
                return collection[index];
            }
        }

        /// <summary>
        /// Concrete iterator class
        /// </summary>
        public sealed class ConcreteIterator : IIterator
        {
            //Iterators need to reference collection objects in order to traverse collection objects.
            private ConcreteTroopQueue _list;
            private int _index;

            public ConcreteIterator(ConcreteTroopQueue list)
            {
                _list = list;
                _index = 0;
            }

            public bool MoveNext()
            {
                if (_index < _list.Length)
                {
                    return true;
                }
                return false;
            }

            public Object GetCurrent()
            {
                return _list.GetElement(_index);
            }

            public void Reset()
            {
                _index = 0;
            }

            public void Next()
            {
                if (_index < _list.Length)
                {
                    _index++;
                }
            }
        }

        static void Main(string[] args)
        {
            #region Iterator mode
            IIterator iterator;
            ITroopQueue list = new ConcreteTroopQueue();
            iterator = list.GetIterator();
            while (iterator.MoveNext())
            {
                string name = (string)iterator.GetCurrent();
                Console.WriteLine(name);
                iterator.Next();
            }

            Console.Read();
            #endregion
        }
    }

The operation results are as follows:

III. key points of the implementation of iterator mode

1) iterative abstraction: access the content of an aggregate object without exposing its internal representation.

2) iterative polymorphism: provide a unified interface for traversing different set structures, so as to support the same algorithm to operate on different set structures.

3) consider the robustness of iterators: changing the set structure of iterators while traversing will cause problems.

3.1 advantages of iterator mode

1) the iterator pattern makes it possible to access the contents of an aggregate object without exposing its internal representation, i.e. iterative abstraction.

2) the iterator pattern provides a uniform interface for traversing different set structures, thus supporting the same algorithm to operate on different set structures.

3.2 disadvantages of iterator mode

1) the iterator pattern changes the set structure of the iterator at the same time of traversal, which will lead to exceptions. Therefore, using foreach statement can only traverse the set, not change the elements in the set at the same time of traversal.

3.3. Use scenario of iterator mode

1) access the content of an aggregate object without exposing its internal representation.

2) support multiple traversal of aggregate objects.

3) provide a unified interface for traversing different aggregation structures (that is, support polymorphic iteration).

IV. implementation of iterator pattern in. NET

There is such a namespace in mscorlib assembly: System.Collections, in which the implementation of iterator pattern has already existed. For aggregation and iterator interfaces already exist, where

IEnumerator plays the role of iterator. Its implementation is as follows:

public interface IEnumerator
{
    object Current
    {
        get;
    }

    bool MoveNext();

    void Reset();
}

Property Current returns the element in the Current collection; Reset() method restores the position pointed to by initialization; MoveNext() method returns a value of true, indicating that the iterator successfully advances to the next element in the collection, and returns a value of false

Indicates that it is already at the end of the collection and can be used to provide traversal of elements.

In. Net, IEnumerable plays the role of abstract aggregation. There is only one GetEnumerator() method. If the collection object needs to have the function of iterative traversal, the interface must be implemented.

public interface IEnumerable
{
    IEumerator GetEnumerator();
}

Abstract Aggregate role (Aggregate) and abstract Iterator role (Iterator) are IEnumerable interface and IEnumerator interface respectively. Concrete Aggregate roles include Queue and BitArray

The codes are as follows:

    public sealed class BitArray : ICollection, IEnumerable, ICloneable
    {
        [Serializable]
        private class BitArrayEnumeratorSimple : IEnumerator, ICloneable
        {
            private BitArray bitarray;

            private int index;

            private int version;

            private bool currentElement;

            public virtual object Current
            {
                get
                {
                    if (this.index == -1)
                    {
                        throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_EnumNotStarted"));
                    }
                    if (this.index >= this.bitarray.Count)
                    {
                        throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_EnumEnded"));
                    }
                    return this.currentElement;
                }
            }

            internal BitArrayEnumeratorSimple(BitArray bitarray)
            {
                this.bitarray = bitarray;
                this.index = -1;
                this.version = bitarray._version;
            }

            public object Clone()
            {
                return base.MemberwiseClone();
            }

            public virtual bool MoveNext()
            {
                if (this.version != this.bitarray._version)
                {
                    throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_EnumFailedVersion"));
                }
                if (this.index < this.bitarray.Count - 1)
                {
                    this.index++;
                    this.currentElement = this.bitarray.Get(this.index);
                    return true;
                }
                this.index = this.bitarray.Count;
                return false;
            }

            public void Reset()
            {
                if (this.version != this.bitarray._version)
                {
                    throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_EnumFailedVersion"));
                }
                this.index = -1;
            }
        }

        private const int BitsPerInt32 = 32;

        private const int BytesPerInt32 = 4;

        private const int BitsPerByte = 8;

        private int[] m_array;

        private int m_length;

        private int _version;

        [NonSerialized]
        private object _syncRoot;

        private const int _ShrinkThreshold = 256;

        [__DynamicallyInvokable]
        public bool this[int index]
        {
            [__DynamicallyInvokable]
            get
            {
                return this.Get(index);
            }
            [__DynamicallyInvokable]
            set
            {
                this.Set(index, value);
            }
        }

        [__DynamicallyInvokable]
        public int Length
        {
            [__DynamicallyInvokable]
            get
            {
                return this.m_length;
            }
            [__DynamicallyInvokable]
            set
            {
                if (value < 0)
                {
                    throw new ArgumentOutOfRangeException("value", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
                }
                int arrayLength = BitArray.GetArrayLength(value, 32);
                if (arrayLength > this.m_array.Length || arrayLength + 256 < this.m_array.Length)
                {
                    int[] array = new int[arrayLength];
                    Array.Copy(this.m_array, array, (arrayLength > this.m_array.Length) ? this.m_array.Length : arrayLength);
                    this.m_array = array;
                }
                if (value > this.m_length)
                {
                    int num = BitArray.GetArrayLength(this.m_length, 32) - 1;
                    int num2 = this.m_length % 32;
                    if (num2 > 0)
                    {
                        this.m_array[num] &= (1 << num2) - 1;
                    }
                    Array.Clear(this.m_array, num + 1, arrayLength - num - 1);
                }
                this.m_length = value;
                this._version++;
            }
        }

        public int Count
        {
            get
            {
                return this.m_length;
            }
        }

        public object SyncRoot
        {
            get
            {
                if (this._syncRoot == null)
                {
                    Interlocked.CompareExchange<object>(ref this._syncRoot, new object(), null);
                }
                return this._syncRoot;
            }
        }

        public bool IsReadOnly
        {
            get
            {
                return false;
            }
        }

        public bool IsSynchronized
        {
            get
            {
                return false;
            }
        }

        private BitArray()
        {
        }

        [__DynamicallyInvokable]
        public BitArray(int length) : this(length, false)
        {
        }

        [__DynamicallyInvokable]
        public BitArray(int length, bool defaultValue)
        {
            if (length < 0)
            {
                throw new ArgumentOutOfRangeException("length", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
            }
            this.m_array = new int[BitArray.GetArrayLength(length, 32)];
            this.m_length = length;
            int num = defaultValue ? -1 : 0;
            for (int i = 0; i < this.m_array.Length; i++)
            {
                this.m_array[i] = num;
            }
            this._version = 0;
        }

        [__DynamicallyInvokable]
        public BitArray(byte[] bytes)
        {
            if (bytes == null)
            {
                throw new ArgumentNullException("bytes");
            }
            if (bytes.Length > 268435455)
            {
                throw new ArgumentException(Environment.GetResourceString("Argument_ArrayTooLarge", new object[]
                {
                    8
                }), "bytes");
            }
            this.m_array = new int[BitArray.GetArrayLength(bytes.Length, 4)];
            this.m_length = bytes.Length * 8;
            int num = 0;
            int num2 = 0;
            while (bytes.Length - num2 >= 4)
            {
                this.m_array[num++] = ((int)(bytes[num2] & 255) | (int)(bytes[num2 + 1] & 255) << 8 | (int)(bytes[num2 + 2] & 255) << 16 | (int)(bytes[num2 + 3] & 255) << 24);
                num2 += 4;
            }
            switch (bytes.Length - num2)
            {
            case 1:
                goto IL_103;
            case 2:
                break;
            case 3:
                this.m_array[num] = (int)(bytes[num2 + 2] & 255) << 16;
                break;
            default:
                goto IL_11C;
            }
            this.m_array[num] |= (int)(bytes[num2 + 1] & 255) << 8;
            IL_103:
            this.m_array[num] |= (int)(bytes[num2] & 255);
            IL_11C:
            this._version = 0;
        }

        [__DynamicallyInvokable]
        public BitArray(bool[] values)
        {
            if (values == null)
            {
                throw new ArgumentNullException("values");
            }
            this.m_array = new int[BitArray.GetArrayLength(values.Length, 32)];
            this.m_length = values.Length;
            for (int i = 0; i < values.Length; i++)
            {
                if (values[i])
                {
                    this.m_array[i / 32] |= 1 << i % 32;
                }
            }
            this._version = 0;
        }

        [__DynamicallyInvokable]
        public BitArray(int[] values)
        {
            if (values == null)
            {
                throw new ArgumentNullException("values");
            }
            if (values.Length > 67108863)
            {
                throw new ArgumentException(Environment.GetResourceString("Argument_ArrayTooLarge", new object[]
                {
                    32
                }), "values");
            }
            this.m_array = new int[values.Length];
            this.m_length = values.Length * 32;
            Array.Copy(values, this.m_array, values.Length);
            this._version = 0;
        }

        [__DynamicallyInvokable]
        public BitArray(BitArray bits)
        {
            if (bits == null)
            {
                throw new ArgumentNullException("bits");
            }
            int arrayLength = BitArray.GetArrayLength(bits.m_length, 32);
            this.m_array = new int[arrayLength];
            this.m_length = bits.m_length;
            Array.Copy(bits.m_array, this.m_array, arrayLength);
            this._version = bits._version;
        }

        [__DynamicallyInvokable]
        public bool Get(int index)
        {
            if (index < 0 || index >= this.Length)
            {
                throw new ArgumentOutOfRangeException("index", Environment.GetResourceString("ArgumentOutOfRange_Index"));
            }
            return (this.m_array[index / 32] & 1 << index % 32) != 0;
        }

        [__DynamicallyInvokable]
        public void Set(int index, bool value)
        {
            if (index < 0 || index >= this.Length)
            {
                throw new ArgumentOutOfRangeException("index", Environment.GetResourceString("ArgumentOutOfRange_Index"));
            }
            if (value)
            {
                this.m_array[index / 32] |= 1 << index % 32;
            }
            else
            {
                this.m_array[index / 32] &= ~(1 << index % 32);
            }
            this._version++;
        }

        [__DynamicallyInvokable]
        public void SetAll(bool value)
        {
            int num = value ? -1 : 0;
            int arrayLength = BitArray.GetArrayLength(this.m_length, 32);
            for (int i = 0; i < arrayLength; i++)
            {
                this.m_array[i] = num;
            }
            this._version++;
        }

        [__DynamicallyInvokable]
        public BitArray And(BitArray value)
        {
            if (value == null)
            {
                throw new ArgumentNullException("value");
            }
            if (this.Length != value.Length)
            {
                throw new ArgumentException(Environment.GetResourceString("Arg_ArrayLengthsDiffer"));
            }
            int arrayLength = BitArray.GetArrayLength(this.m_length, 32);
            for (int i = 0; i < arrayLength; i++)
            {
                this.m_array[i] &= value.m_array[i];
            }
            this._version++;
            return this;
        }

        [__DynamicallyInvokable]
        public BitArray Or(BitArray value)
        {
            if (value == null)
            {
                throw new ArgumentNullException("value");
            }
            if (this.Length != value.Length)
            {
                throw new ArgumentException(Environment.GetResourceString("Arg_ArrayLengthsDiffer"));
            }
            int arrayLength = BitArray.GetArrayLength(this.m_length, 32);
            for (int i = 0; i < arrayLength; i++)
            {
                this.m_array[i] |= value.m_array[i];
            }
            this._version++;
            return this;
        }

        [__DynamicallyInvokable]
        public BitArray Xor(BitArray value)
        {
            if (value == null)
            {
                throw new ArgumentNullException("value");
            }
            if (this.Length != value.Length)
            {
                throw new ArgumentException(Environment.GetResourceString("Arg_ArrayLengthsDiffer"));
            }
            int arrayLength = BitArray.GetArrayLength(this.m_length, 32);
            for (int i = 0; i < arrayLength; i++)
            {
                this.m_array[i] ^= value.m_array[i];
            }
            this._version++;
            return this;
        }

        [__DynamicallyInvokable]
        public BitArray Not()
        {
            int arrayLength = BitArray.GetArrayLength(this.m_length, 32);
            for (int i = 0; i < arrayLength; i++)
            {
                this.m_array[i] = ~this.m_array[i];
            }
            this._version++;
            return this;
        }

        public void CopyTo(Array array, int index)
        {
            if (array == null)
            {
                throw new ArgumentNullException("array");
            }
            if (index < 0)
            {
                throw new ArgumentOutOfRangeException("index", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
            }
            if (array.Rank != 1)
            {
                throw new ArgumentException(Environment.GetResourceString("Arg_RankMultiDimNotSupported"));
            }
            if (array is int[])
            {
                Array.Copy(this.m_array, 0, array, index, BitArray.GetArrayLength(this.m_length, 32));
                return;
            }
            if (array is byte[])
            {
                int arrayLength = BitArray.GetArrayLength(this.m_length, 8);
                if (array.Length - index < arrayLength)
                {
                    throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen"));
                }
                byte[] array2 = (byte[])array;
                for (int i = 0; i < arrayLength; i++)
                {
                    array2[index + i] = (byte)(this.m_array[i / 4] >> i % 4 * 8 & 255);
                }
                return;
            }
            else
            {
                if (!(array is bool[]))
                {
                    throw new ArgumentException(Environment.GetResourceString("Arg_BitArrayTypeUnsupported"));
                }
                if (array.Length - index < this.m_length)
                {
                    throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen"));
                }
                bool[] array3 = (bool[])array;
                for (int j = 0; j < this.m_length; j++)
                {
                    array3[index + j] = ((this.m_array[j / 32] >> j % 32 & 1) != 0);
                }
                return;
            }
        }

        public object Clone()
        {
            return new BitArray(this.m_array)
            {
                _version = this._version,
                m_length = this.m_length
            };
        }

        [__DynamicallyInvokable]
        public IEnumerator GetEnumerator()
        {
            return new BitArray.BitArrayEnumeratorSimple(this);
        }

        private static int GetArrayLength(int n, int div)
        {
            if (n <= 0)
            {
                return 0;
            }
            return (n - 1) / div + 1;
        }
    }

There are many types, not to be listed one by one. You can view the source code. Each element can find the corresponding element on the construction diagram of the iterator pattern. The specific types and code screenshots are as follows:

V. summary

If you want to learn more about the iterator pattern, you can take a good look at its implementation in the. Net framework, which will surely benefit you a lot.

Topics: C#