Patterns. Паттерн Iterator. Реализация на Java




Паттерн Iterator. Реализация на Java с поддержкой полиморфного контейнера и полиморфного итератора


Содержание


Поиск на других ресурсах:

1. Структурная схема паттерна Iterator с привязкой к коду Java

На рисунке 1 изображена схема паттерна Iterator с поддержкой полиморфного контейнера и полиморфного итератора.

Схема паттерна Iterator с поддержкой Java-кода

Рисунок 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. Добавление нового итератора. Последовательность шагов

Чтобы добавить новый конкретный итератор (например, обратный итератор), нужно выполнить следующие шаги.

  1. Создать класс, реализующий интерфейс IIterator<T>.
  2. Объявить внутренние данные в классе, специфические для данного итератора.
  3. Объявить реализацию методов, объявленных в интерфейсе IIterator<T>.
  4. Реализовать дополнительные методы.

Примерный код для класса 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. Добавление нового контейнера. Последовательность шагов

Чтобы добавить новый конкретный контейнер, нужно выполнить следующие шаги.

  1. Создать класс, реализующий интерфейс IAggregate<T>.
  2. Объявить внутренние данные класса-контейнера. Это могут быть массив, список, дерево, множество тому подобное.
  3. Объявить методы, обеспечивающие основной функционал контейнера (добавить новый элемент, удалить элемент и т.д.).
  4. Реализовать методы, объявленные в интерфейсе IAggregate<T>.
  5. Реализовать дополнительные методы, обеспечивающие функционал контейнера.

Например, класс с именем 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. Дополнительные специализированные методы
  // ...
}

 


Связанные темы