Паттерн Iterator. Реализация на C#
Перед изучением данной темы рекомендуется ознакомиться с темой:
Содержание
- 1. Структура паттерна Iterator в кодах C#. Рисунок
- 2. Программирование составляющих, определяющих структуру паттерна Iterator
- 3. Код клиента. Демонстрация паттерна
- Связанные темы
Поиск на других ресурсах:
1. Структура паттерна Iterator в кодах C#. Рисунок
Информацию о паттерне Iterator и способах его реализации можно получить здесь.
На рисунке 1 изображена структура паттерна Iterator с фрагментами кодов на языке C#. Данная структура может быть использована для любого количества контейнеров (полиморфный контейнер) и любого количества итераторов (полиморфный итератор).
Для того, чтобы добавить свой специфический контейнер, нужно в классе реализовать интерфейс IAggregate. В этом интерфейсе объявляется единственный метод CreateIterator(). Этот метод возвращает итератор для контейнера.
Рисунок 1. Структура паттерна Iterator
⇑
2. Программирование составляющих, определяющих структуру паттерна Iterator
Для реализации паттерна Iterator нужно разработать следующие интерфейсы и классы:
- интерфейс IAggregate, содержащий объявления метода CreateIterator();
- интерфейс IIterator, содержащий базовые методы получения итератора и движения по контейнеру;
- один или несколько конкретных итераторов. Здесь могут быть объявлены итераторы, осуществляющих различные стратегии обхода. Все итераторы реализуют интерфейс IIterator;
- один или несколько классов, реализующих интерфейс IAggregate.
⇑
2.1. Интерфейс контейнера IAggregate<T>
Прежде всего объявляется интерфейс контейнера IAggregate<T>, содержащий объявления следующих методов:
- CreateIterator() — возвращает итератор для контейнера;
- Count() — возвращает количество элементов контейнера;
- GetItem() — возвращает элемент контейнера по его индексу.
Согласно синтаксису C# все эти методы должны быть обязательно реализованы в классах, реализующих интерфейс IAggregate<T>.
После ввода интерфейса листинг программы следующий:
using System; namespace ConsoleApp17 { // Реализация паттерна Iterator на C#. // Пример для консольного приложения // 1. Объявить интерфейс контейнера с поддержкой обобщенного типа T interface IAggregate<T> { // Вернуть итератор на контейнер IIterator<T> CreateIterator(); // Количество элементов контейнера long Count(); // Вернуть элемент по его индексу T GetItem(long index); } class Program { static void Main(string[] args) { } } }
Интерфейс служит связующим звеном между подклассами, что его реализуют и интерфейсом IIterator<T>.
⇑
2.2. Интерфейс итератора IIterator<T>
Для обеспечения возможности реализации различных видов итераторов, нужно объявить интерфейс итератора IIterator<T>. Этот интерфейс будет содержать общие методы для всех классов итераторов, которые будут его реализовывать.
В нашем случае в интерфейс IIterator<T> вводятся следующие объявления методов:
- First() — перейти на первый элемент контейнера;
- Next() — переместить курсор на следующий элемент контейнера;
- IsDone() — проверка, конец ли списка;
- CurrentItem() — получить текущий элемент по курсору.
По желанию можно расширить список методов для унаследованных итераторов. Например, добавить метод Previous() для перехода к предыдущему элементу контейнера.
Сокращенный код программы после ввода интерфейса следующий.
using System; namespace ConsoleApp17 { // Реализация паттерна Iterator на C#. // Пример для консольного приложения // 1. Объявить интерфейс контейнера с поддержкой обобщенного типа T interface IAggregate<T> { ... } // 2. Объявить интерфейс итератора для типа T interface IIterator<T> { // Переход на первый элемент контейнера void First(); // Переход на следующий элемент контейнера void Next(); // Получить текущий элемент T CurrentItem(); // Проверка, указывает ли курсор на конец контейнера bool IsDone(); } ... }
⇑
2.3. Конкретный итератор ConcreteIterator<T>
На этом этапе создается один или несколько классов, реализующих интерфейс IIterator<T>. В нашем случае создается один класс с именем ConcreteIterator1<T>. Этот класс реализует прямой итератор.
В конкретном итераторе реализованы следующие программные элементы:
- внутреннее поле aggregate — это ссылка на контейнер, для которого создается данный итератор;
- внутреннее поле current — значение (позиция), которое указывает на один из элементов контейнера. Это курсор;
- конструктор, который получает агрегатный объект в качестве параметра;
- методы First(), Next(), IsDone() и CurrentItem(), которые переопределяют методы интерфейса IIterator<T>.
using System; namespace ConsoleApp17 { // Реализация паттерна Iterator на C#. // Пример для консольного приложения // 1. Объявить интерфейс контейнера с поддержкой обобщенного типа T interface IAggregate<T> { ... } // 2. Объявить интерфейс итератора для типа T interface IIterator<T> { ... } // 3. Конкретный итератор class ConcreteIterator1<T> : IIterator<T> { // внутренние данные private IAggregate<T> aggregate; // Ссылка на агрегат private long current; // текущая позиция // Конструктор, получающий агрегированный объект public ConcreteIterator1(IAggregate<T> obj) { aggregate = obj; current = 0; } // Перевод курсора в начало списка virtual public void First() { current = 0; } // Перевод курсора на следующий элемент списка virtual public void Next() { current++; } // Проверка, конец ли списка virtual public bool IsDone() { return current >= aggregate.Count(); } // Вернуть текущий элемент списка virtual public T CurrentItem() { if (!IsDone()) return aggregate.GetItem(current); else { throw new NotImplementedException("Error"); } } } ... }
По вышеприведенному образцу кода можно добавлять другие виды итераторов с произвольными именами. Обязательным условием является реализация (наследование) интерфейса IIterator<T>.
Если нужно добавить собственный итератор то выполняются следующие шаги:
- выбрать имя итератора и объявить класс этого итератора (например, ConcreteIterator2<T> или другое). Класс должен реализовывать (быть унаследован от) интерфейс IIterator<T>;
- реализовать внутренние данные и один или несколько конструкторов;
- реализовать методы First(), Next(), IsDone(), CurrentItem() объявленные в интерфейсе IIterator<T>;
- по желанию объявить дополнительные методы.
Одной из разновидностей итератора является обратной итератор который просматривает контейнер с конца до начала. В нашем случае, если бы нужно было реализовать обратной итератор (просматривает элементы контейнера от конца до начала), то некоторые методы должны иметь иную трактовку:
- метод First() реализовывал бы переход на последний элемент контейнера;
- метод Next() обеспечивал бы перемещение курсора на предыдущий элемент контейнера.
⇑
2.4. Класс ConcreteAggregate<T>
Объявленный в п. 2.1 интерфейс IAggregate<T> является общим для всех классов, реализующих конкретный агрегат (контейнер). Благодаря полиморфизму в программе может быть реализовано любое количество конкретных агрегатов. Как известно, в каждом классе контейнера данные представлены в виде набора элементов (список, массив, дерево и т.д.).
Если создается класс конкретного контейнера (агрегата), то к нему предъявляется ряд требований:
- класс обязательно должен реализовывать (наследовать) интерфейс IAggregate<T>;
- класс должен содержать минимальный функционал для оперирования набором элементов;
- класс должен реализовывать методы интерфейса IAggregate<T>. Обязательным методом является метод CreateIterator().
В нашем случае объявляется класс ConcreteAggregate1<T>, который реализует интерфейс IAggregate<T>. Класс содержит следующие основные составляющие:
- внутреннее поле-массив array обобщенного типа T. Вместо массива может использоваться любая другая структура данных: список, дерево, множество и тому подобное;
- два конструктора;
- метод Append() — добавляет новый элемент в конец массива;
- метод Remove() — удаляет элемент из массива по указанной позиции;
- метод Print() — выводит массив.
- методы, объявленные в интерфейсе IAggregate<T>: CreateIterator(), Count(), GetItem().
Сокращенный фрагмент программы с кодом класса ConcreteAggregate1<T> имеет вид.
using System; namespace ConsoleApp17 { // Реализация паттерна Iterator на C#. // Пример для консольного приложения // 1. Объявить интерфейс контейнера с поддержкой обобщенного типа T interface IAggregate<T> { ... } // 2. Объявить интерфейс итератора для типа T interface IIterator<T> { ... } // 3. Конкретный итератор class ConcreteIterator1<T> : IIterator<T> { ... } // 4. Конкретный контейнер для элементов типа T class ConcreteAggregate1<T>:IAggregate<T> { // Внутренние поля private T[] array; // динамический массив элементов типа T // Конструкторы // Конструктор, получающий внешний массив public ConcreteAggregate1(T[] _array) { array = _array; } // Конструктор, создающий пустой массив public ConcreteAggregate1() { array = null; } // Метод, добавляющий элемент в конец контейнера public void Append(T item) { T[] array2 = array; array = new T[array2.Length + 1]; array2.CopyTo(array, 0); array[array.Length - 1] = item; } // Удаляет элемент из контейнера в позиции index public void Remove(long index) { T[] array2 = array; array = new T[array2.Length - 1]; for (long i = 0; i < index; i++) array[i] = array2[i]; for (long i = index + 1; i < array2.Length; i++) array[i - 1] = array2[i]; } // Вывести содержимое контейнера public void Print(string text) { Console.WriteLine(text + ":"); for (int i = 0; i < array.Length; i++) Console.Write(array[i] + " "); Console.WriteLine(); } // Реализация методов интерфейса IAggregate<T> // Вернуть итератор public IIterator<T> CreateIterator() { return new ConcreteIterator1<T>(this); } // Количество элементов public long Count() { return array.Length; } // Вернуть отдельный элемент public T GetItem(long index) { if ((index >= 0) && (index < array.Length)) return array[index]; else throw new NotImplementedException("Error. Bad index"); } } }
⇑
3. Код клиента. Демонстрация паттерна
Ниже приведены клиентский код, демонстрирующий получение итератора и его использование.
using System; namespace ConsoleApp17 { // Реализация паттерна Iterator на C#. // Пример для консольного приложения // 1. Объявить интерфейс контейнера с поддержкой обобщенного типа T interface IAggregate<T> { ... } // 2. Объявить интерфейс итератора для типа T interface IIterator<T> { ... } // 3. Конкретный итератор class ConcreteIterator1<T> : IIterator<T> { ... } // 4. Конкретный контейнер для элементов типа T class ConcreteAggregate1<T>:IAggregate<T> { ... } class Program { static void Main(string[] args) { // Тестирование паттерна Iterator - код клиента // 1. Создать массив чисел double[] A = { 2.3, 3.2, 4.4, 5.1, 8.2 }; // 2. Создать экземпляр контейнера на основе массива A ConcreteAggregate1<double> ag1 = new ConcreteAggregate1<double>(A); ag1.Print("ag1"); // вывод контейнера // 3. Создать итератор для экземпляра ag1 ConcreteIterator1<double> it1 = (ConcreteIterator1<double>)ag1.CreateIterator(); // 4. Демонстрация работы итератора Console.WriteLine("----------------------"); Console.WriteLine("Access using iterator:"); it1.First(); while (!it1.IsDone()) { Console.Write("{0} ", (double)it1.CurrentItem()); it1.Next(); } Console.WriteLine(); Console.ReadKey(); } } }
После запуска, программа выдает следующий результат
ag1: 2.3 3.2 4.4 5.1 8.2 ---------------------- Access using iterator: 2.3 3.2 4.4 5.1 8.2
⇑
Связанные темы
- Общие сведения. Способы реализации. Структурная схема. Пример на C++
- Паттерн Iterator. Особенности реализации на C++ для полиморфного контейнера и полиморфного итератора
- Внешний и внутренний итераторы. Реализация на C++
- Паттерн Iterator. Реализация на Java с поддержкой полиморфного контейнера и полиморфного итератора
⇑