Статьи

Пример шаблона дизайна итератора

Эта статья является частью нашего курса Академии под названием « Шаблоны проектирования Java» .

В этом курсе вы изучите огромное количество шаблонов проектирования и увидите, как они реализуются и используются в Java. Вы поймете причины, почему шаблоны так важны, и узнаете, когда и как применять каждый из них. Проверьте это здесь !

1. Введение

Агрегированный объект, такой как список, должен предоставить вам доступ к его элементам, не раскрывая его внутреннюю структуру. Более того, вы можете просмотреть список различными способами, в зависимости от того, чего вы хотите достичь. Но вы, вероятно, не хотите разбрасывать интерфейс List операциями для различных обходов, даже если вы можете предвидеть те, которые вам понадобятся. Вам также может потребоваться, чтобы в одном списке было несколько ожидающих обходов.

Шаблон Iterator позволяет вам делать все это. Основная идея в этом шаблоне состоит в том, чтобы взять на себя ответственность за доступ и обход из объекта списка и поместить его в объект итератора. Класс Iterator определяет интерфейс для доступа к элементам списка. Объект итератора отвечает за отслеживание текущего элемента; то есть он знает, какие элементы уже пройдены.

2. Что такое шаблон итератора

Цель шаблона проектирования итератора — предоставить способ последовательного доступа к элементам агрегатного объекта без раскрытия его базового представления.

Шаблон Iterator позволяет клиентскому объекту обращаться к содержимому контейнера последовательно, не имея никаких знаний о внутреннем представлении его содержимого. Термин «контейнер», использованный выше, может быть просто определен как набор данных или объектов. Объекты в контейнере, в свою очередь, могут быть коллекциями, что делает его коллекцией коллекций.

Шаблон Iterator позволяет клиентскому объекту проходить через эту коллекцию объектов (или контейнер), не имея контейнера, чтобы показать, как данные хранятся внутри. Для этого шаблон итератора предполагает, что объект-контейнер должен быть спроектирован так, чтобы предоставлять открытый интерфейс в форме объекта итератора для различных клиентских объектов для доступа к его содержимому. Объект Iterator содержит открытые методы, позволяющие клиентскому объекту перемещаться по списку объектов в контейнере.

Рисунок 1 - Диаграмма классов

Рисунок 1 — Диаграмма классов

Итератор

  • Определяет интерфейс для доступа и обхода элементов.

ConcreteIterator

  • Реализует интерфейс Iterator.
  • Отслеживает текущую позицию в обходе агрегата.

заполнитель

  • Определяет интерфейс для создания объекта Iterator.

ConcreteAggregate

  • Реализует интерфейс создания Iterator для возврата экземпляра правильного ConcreteIterator.

3. Реализация шаблона проектирования итератора

Давайте реализуем шаблон проектирования Iterator, используя класс Shape . Мы будем хранить и повторять объекты Shape используя итератор.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package com.javacodegeeks.patterns.iteratorpattern;
 
public class Shape {
 
    private int id;
    private String name;
     
    public Shape(int id, String name){
        this.id = id;
        this.name = name;
    }
     
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
     
    @Override
    public String toString(){
        return "ID: "+id+" Shape: "+name;
    }
     
}

Простой класс Shape имеет id и name качестве своих атрибутов.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
package com.javacodegeeks.patterns.iteratorpattern;
 
public class ShapeStorage {
     
    private Shape []shapes = new Shape[5];
    private int index;
     
    public void addShape(String name){
        int i = index++;
        shapes[i] = new Shape(i,name);
    }
     
    public Shape[] getShapes(){
        return shapes;
    }
}

Приведенный выше класс используется для хранения объектов Shape . Класс содержит массив типа Shape , для простоты мы инициализировали этот массив до 5. Метод addShape используется для добавления объекта Shape в массив и увеличения индекса на единицу. Метод getShapes возвращает массив типа Shape .

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
package com.javacodegeeks.patterns.iteratorpattern;
 
import java.util.Iterator;
 
public class ShapeIterator implements Iterator<Shape>{
 
    private Shape [] shapes;
    int pos;
     
    public ShapeIterator(Shape []shapes){
        this.shapes = shapes;
    }
    @Override
    public boolean hasNext() {
        if(pos >= shapes.length || shapes[pos] == null)
            return false;
        return true;
    }
 
    @Override
    public Shape next() {
        return shapes[pos++];
    }
 
    @Override
    public void remove() {
        if(pos <=0 )
            throw new IllegalStateException("Illegal position");
        if(shapes[pos-1] !=null){
            for (int i= pos-1; i<(shapes.length-1);i++){
                shapes[i] = shapes[i+1];
            }
            shapes[shapes.length-1] = null;
        }
    }
}

Приведенный выше класс является Iterator класса Shape . Класс реализует интерфейс Iterator и определяет все методы интерфейса Iterator .

Метод hasNext возвращает boolean если остался элемент. Метод next возвращает следующий элемент из коллекции, а метод remove удаляет текущий элемент из коллекции.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package com.javacodegeeks.patterns.iteratorpattern;
 
public class TestIteratorPattern {
 
    public static void main(String[] args) {
        ShapeStorage storage = new ShapeStorage();
        storage.addShape("Polygon");
        storage.addShape("Hexagon");
        storage.addShape("Circle");
        storage.addShape("Rectangle");
        storage.addShape("Square");
         
        ShapeIterator iterator = new ShapeIterator(storage.getShapes());
        while(iterator.hasNext()){
            System.out.println(iterator.next());
        }
        System.out.println("Apply removing while iterating...");
        iterator = new ShapeIterator(storage.getShapes());
        while(iterator.hasNext()){
            System.out.println(iterator.next());
            iterator.remove();
        }
    }
 
}

Приведенный выше код приведет к следующему выводу:

1
2
3
4
5
6
7
8
9
ID: 0 Shape: Polygon
ID: 1 Shape: Hexagon
ID: 2 Shape: Circle
ID: 3 Shape: Rectangle
ID: 4 Shape: Square
Apply removing while iterating...
ID: 0 Shape: Polygon
ID: 2 Shape: Circle
ID: 4 Shape: Square

В приведенном выше классе мы создали объект ShapeStorage и сохранили в нем объекты Shape . Затем мы создали объект ShapeIterator и присвоили ему формы. Мы повторили дважды, сначала без вызова метода remove, а затем с методом remove.

Выходные данные показывают влияние метода удаления. На первой итерации итератор печатает все фигуры, но когда вызывается метод remove, он сначала печатает текущую фигуру и перемещается к следующей фигуре. Затем мы вызвали метод remove, который удаляет текущую фигуру, а затем итератор указывает на следующий доступный объект формы.

Поэтому в выводе после «Применить удаление во время итерации…» отображаются только объекты 0, 2 и 4 фигур.

4. Внутренние и внешние итераторы

Итератор может быть выполнен как внутренний итератор или как внешний итератор.

4.1 Внутренние итераторы

  • Сама коллекция предлагает методы, позволяющие клиенту посещать различные объекты в коллекции. Например, класс java.util.ResultSet содержит данные, а также предлагает такие методы, как next для навигации по списку элементов.
  • В любой момент времени в коллекции может быть только один итератор.
  • Коллекция должна поддерживать или сохранять состояние итерации.

4.2 Внешние итераторы

  • Функциональность итерации отделена от коллекции и находится внутри другого объекта, называемого итератором. Обычно сама коллекция возвращает соответствующий объект итератора клиенту в зависимости от входных данных клиента. Например, класс java.util.Vector имеет свой итератор, определенный в виде отдельного объекта типа Enumeration . Этот объект возвращается клиентскому объекту в ответ на вызов метода elements() .
  • В любой момент времени в данной коллекции может быть несколько итераторов.
  • Затраты на хранение состояния итерации не связаны с коллекцией. Он принадлежит эксклюзивному объекту Iterator.

5. Когда использовать шаблон проектирования итератора

Используйте шаблон Iterator:

  • Для доступа к содержимому совокупного объекта, не раскрывая его внутреннего представления.
  • Для поддержки множественных обходов агрегатных объектов.
  • Обеспечить единый интерфейс для обхода различных агрегатных структур (то есть для поддержки полиморфной итерации).

6. Шаблон итератора в JDK

  • java.util.Iterator
  • java.util.Enumeration

7. Загрузите исходный код

Это был урок по шаблону дизайна итераторов. Вы можете скачать исходный код здесь: IteratorPattern-Project