Паттерн Iterator. Реализация на Java с поддержкой полиморфного контейнера и полиморфного итератора
Содержание
- 1. Структурная схема паттерна Iterator с привязкой к коду Java
- 2. Программная реализация
- 3. Добавление нового итератора. Последовательность шагов
- 4. Добавление нового контейнера. Последовательность шагов
- Связанные темы
Поиск на других ресурсах:
1. Структурная схема паттерна Iterator с привязкой к коду Java
На рисунке 1 изображена схема паттерна Iterator с поддержкой полиморфного контейнера и полиморфного итератора.
Рисунок 1. Схема паттерна Iterator с поддержкой Java-кода
⇑
2. Программная реализация
2.1. Классы, необходимые для демонстрации паттерна
Для реализации паттерна Iterator, изображенного на рисунке 1, нужен следующий минимальный набор интерфейсов и классов:
- обобщенный интерфейс IAggregate<T> — определяет базовые методы, необходимые для реализации контейнера (агрегата). Этот интерфейс будут реализовывать (implements) классы конкретных контейнеров (списков, массивов и т.п.). Благодаря полиморфизму для клиента будут подгружаться методы по соответствующему конкретному контейнеру;
- обобщенный интерфейс IIterator<T> — определяет перечень методов, необходимых для обеспечения работы итератора. Каждый конкретный итератор будет реализован в классах, реализующих этот интерфейс;
- один или несколько обобщенных классов, которые реализуют интерфейс IAggregate<T>. Эти классы представляют конкретные контейнеры. Каждый из контейнеров представляет собой некоторый набор элементов: массив, список и тому подобное. Более подробную информацию о видах контейнерных классов можно получить здесь. В нашем случае демонстрируется разработка одного обобщенного класса с именем ConcreteAggregate<T>;
- один или несколько обобщенных классов, которые реализуют интерфейс IIterator<T>. Эти классы являются итераторами. Как известно, для определенного контейнера может быть несколько итераторов, например прямой итератор, обратный итератор и тому подобное. Более подробно о классификации итераторов можно прочитать здесь;
- класс клиента, будет демонстрировать работу конкретного итератора для конкретного контейнера.
⇑
2.2. Интерфейс IAggregate<T>. Общий интерфейс для всех агрегатов
Первым в программе объявляется интерфейс, который является общим для всех классов агрегатов (контейнеров или контейнерных классов). Агрегатами могут быть списки, массивы, множества и тому подобное.
В нашем случае объявляется интерфейс IAggregate<T>. Здесь T – имя некоторого обобщенного типа.
Текст модуля TestIterator.java, демонстрирующий работу паттерна, пока имеет следующий вид.
// Паттерн Iterator. Реализация на Java // Интерфейс контейнера (агрегата) interface IAggregate<T> { // Метод, возвращающий итератор для контейнера IIterator<T> CreateIterator(); // Метод, возвращающий количество элементов в контейнере int Count(); // Метод, возвращающий элемент в контейнере по его позиции index T GetItem(int index); } public class TestIterator { public static void main(String[] args) { } }
⇑
2.3. Интерфейс IIterator<T>. Обобщенный интерфейс итератора
На этом этапе в программу добавляется интерфейс IIterator<T>. Этот интерфейс должен быть реализован в классах конкретных итераторов.
После ввода в программу интерфейса, сокращенный текст программного модуля следующий.
// Паттерн Iterator. Реализация на Java // Интерфейс контейнера (агрегата) interface IAggregate<T> { ... } // Интерфейс итератора interface IIterator<T> { // Перемотать курсор в начало набора void First(); // Перемотать курсор на следующий элемент void Next(); // Вернуть элемент, на который указывает курсор T CurrentItem(); // Проверка, указывает ли курсор на позицию, // которая находится за последним элементом набора boolean IsDone(); } public class TestIterator { public static void main(String[] args) { } }
⇑
2.4. Класс конкретного итератора ConcreteIterator1<T>
Любой класс конкретного итератора должен реализовывать интерфейс IIterator<T>. В нашем случае класс конкретного итератора имеет имя ConcreteIterator1<T>. В классе конкретного итератора объявляются следующие элементы:
- внутреннее поле aggregate — ссылка на конкретный контейнер;
- внутреннее поле current — курсор, который указывает на некоторый элемент в контейнере;
- конструктор, который получает ссылку на интерфейс IAggregate<T>;
- метод First() — перемещает курсор на первый элемент контейнера (элемент в позиции 0);
- метод Next() — реализует перемещение курсора на одну позицию вперед;
- метод IsDone() — определяет, указывает ли курсор current на позицию, которая установлена за последним элементом;
- метод CurrentItem() — возвращает элемент, на который указывает current.
После добавления класса ConcreteIterator1<T> сокращенный текст программы следующий:
// Паттерн Iterator. Реализация на Java // Интерфейс контейнера (агрегата) interface IAggregate<T> { ... } // Интерфейс итератора interface IIterator<T> { ... } // Класс конкретного итератора class ConcreteIterator1<T> implements IIterator<T> { // Внутренние поля класса private final IAggregate<T> aggregate; // ссылка на контейнер private int current; // текущая позиция курсора // Конструктор для итератора public ConcreteIterator1(IAggregate<T> agg) { aggregate = agg; current = 0; } // Реализация методов, объявленных в интерфейсе IIterator<T> // Перемещение курсора на первый элемент public void First() { current = 0; } // Перемещение курсора на следующий элемент public void Next() { current++; } // Проверка, конец ли набора элементов public boolean IsDone() { return current >= aggregate.Count(); } // Вернуть текущий элемент public T CurrentItem() { if (!IsDone()) return aggregate.GetItem(current); else throw new ArrayIndexOutOfBoundsException("Error."); } } public class TestIterator { public static void main(String[] args) { } }
⇑
2.5. Класс конкретного агрегата
Все классы контейнеров должны реализовывать интерфейс IAggregate<T>. В нашем случае создается класс контейнера ConcreteAggregate1<T>, который представляет собой динамический массив типа ArrayList<T>. Чтобы использовать массив ArrayList<T> нужно импортировать модуль java.util.ArrayList
import java.util.ArrayList;
Класс содержит следующие составляющие:
- внутреннее поле array, которое является динамическим массивом типа ArrayList<T>;
- конструктор, который инициализирует внутренний массив array другим массивом типа T[]. По желанию можно реализовать другие конструкторы;
- метод Append(), который добавляет элемент в конец контейнера;
- метод Remove(), который удаляет элемент из контейнера по его индексу;
- метод Print(), который печатает элементы массива;
- метод CreateIterator() — реализует одноименный метод интерфейса IAggregate<T>. Метод возвращает ссылку на интерфейс IIterator<T>;
- метод Count() — реализация метода из интерфейса IAggregate<T>. Метод возвращает количество элементов в контейнере;
- метод GetItem() — возвращает текущий элемент в контейнере с его позицией.
С учетом вышесказанного, после ввода класса, сокращенный листинг программы следующий.
import java.util.ArrayList; // Паттерн Iterator. Реализация на Java // Интерфейс контейнера (агрегата) interface IAggregate<T> { ... } // Интерфейс итератора interface IIterator<T> { ... } // Класс конкретного итератора class ConcreteIterator1<T> implements IIterator<T> { ... } // Класс, реализующий конкретный контейнер. // Контейнером служит массив типа ArrayList<T> class ConcreteAggregate1<T> implements IAggregate<T> { // Внутренние поля класса private ArrayList<T> array; // массив элементов // Конструктор public ConcreteAggregate1(T[] _array) { array = new ArrayList<T>(); for (int i=0; i<_array.length; i++) array.add(_array[i]); } // Методы оперирования контейнером // Добавить элемент в конец контейнера public void Append(T item) { array.add(item); } // Удалить элемент из контейнера public void Remove(int index) { array.remove(index); } public void Print(String text) { System.out.println(text); for (int i=0; i<array.size(); i++) System.out.print(array.get(i) + " "); System.out.println(); } // Методы интерфейса IAggregate<T> // Вернуть итератор public IIterator<T> CreateIterator() { return new ConcreteIterator1<T>(this); } // Вернуть длину контейнера public int Count() { return array.size(); } // Вернуть текущий элемент public T GetItem(int index) { if ((index>=0)&&(index<array.size())) return array.get(index); else throw new ArrayIndexOutOfBoundsException("Error. Bad index."); } } // Клиентский класс public class TestIterator { public static void main(String[] args) { } }
⇑
2.6. Класс клиента
Клиентский код приведен в функции main(). В клиентском коде тестируется работа паттерна Iterator.
import java.util.ArrayList; // Паттерн Iterator. Реализация на Java // Интерфейс контейнера (агрегата) interface IAggregate<T> { ... } // Интерфейс итератора interface IIterator<T> { ... } // Класс конкретного итератора class ConcreteIterator1<T> implements IIterator<T> { ... } // Класс, реализующий конкретный контейнер. class ConcreteAggregate1<T> implements IAggregate<T> { ... } public class TestIterator { public static void main(String[] args) { // Тестирование паттерна Iterator. Код клиента // 1. Объявить массив данных Double[] AD = { 1.1, 2.3, 8.5, -4.9 }; // 2. Создать контейнер на основе массива AD ConcreteAggregate1<Double> ag1 = new ConcreteAggregate1<Double>(AD); ag1.Print("ag1"); // 3. Создать итератор для контейнера ag1 ConcreteIterator1<Double> it1 = (ConcreteIterator1<Double>) ag1.CreateIterator(); // 4. Продемонстрировать работу итератора System.out.println("-------------------"); System.out.println("Access using iterator."); it1.First(); while (!it1.IsDone()) { System.out.print(it1.CurrentItem()+" "); it1.Next(); } } }
Результат выполнения программы
ag1 1.1 2.3 8.5 -4.9 ------------------- Access using iterator. 1.1 2.3 8.5 -4.9
⇑
3. Добавление нового итератора. Последовательность шагов
Чтобы добавить новый конкретный итератор (например, обратный итератор), нужно выполнить следующие шаги.
- Создать класс, реализующий интерфейс IIterator<T>.
- Объявить внутренние данные в классе, специфические для данного итератора.
- Объявить реализацию методов, объявленных в интерфейсе IIterator<T>.
- Реализовать дополнительные методы.
Примерный код для класса MyNewIterator<T> может быть, например, таким.
class MyNewIterator<T> implements IIterator<T> { // 1. Внутренние данные // ... // 2. Дополнительные методы класса // ... // 3. Методы, которые объявлены в интерфейсе IIterator<T> public void First() { ... } public void Next() { ... } public boolean IsDone() { ... } public T CurrentItem() { ... } // 4. Дополнительные методы интерфейса, специфические для данного ітератора // ... }
⇑
4. Добавление нового контейнера. Последовательность шагов
Чтобы добавить новый конкретный контейнер, нужно выполнить следующие шаги.
- Создать класс, реализующий интерфейс IAggregate<T>.
- Объявить внутренние данные класса-контейнера. Это могут быть массив, список, дерево, множество тому подобное.
- Объявить методы, обеспечивающие основной функционал контейнера (добавить новый элемент, удалить элемент и т.д.).
- Реализовать методы, объявленные в интерфейсе IAggregate<T>.
- Реализовать дополнительные методы, обеспечивающие функционал контейнера.
Например, класс с именем MyNewAggregate<T> может иметь следующую реализацию.
class MyNewAggregate<T> implements IAggregate<T> { // 1. Внутренние поля класса // ... // 2. Конструкторы и методы оперирования контейнером // ... // 3. Методы оперирования контейнером // ... // 4. Методы интерфейса IAggregate<T> // ... public IIterator<T> CreateIterator() { return new MyNewAggregate<T>(this); } public int Count() { ... } public T GetItem(int index) { ... } // 5. Дополнительные специализированные методы // ... }
⇑
Связанные темы
- Общие сведения. Способы реализации. Структурная схема. Пример на C++
- Паттерн Iterator. Особенности реализации на C++ для полиморфного контейнера и полиморфного итератора
- Внешний и внутренний итераторы. Реализация на C++
- Паттерн Iterator. Реализация на C#
⇑