Menu
Coddy logo textTech

Паттерн «Итератор»

Часть раздела Объектно-ориентированное программирование путешествия по Java на Coddy — урок 76 из 87.

Паттерн Итератор (Iterator Pattern) — это поведенческий паттерн проектирования, который предоставляет способ последовательного доступа к элементам коллекции, не раскрывая её внутреннее представление. Независимо от того, работаете ли вы с массивом, связанным списком или деревом, итератор дает вам единообразный способ обхода элементов.

Паттерн отделяет логику обхода от самой коллекции. Он включает в себя два основных компонента: интерфейс Iterator, который определяет методы для обхода элементов, и Iterable (или Aggregate), который создает итераторы для своей коллекции:

interface Iterator<T> {
    boolean hasNext();
    T next();
}

interface Container<T> {
    Iterator<T> createIterator();
}

class BookShelf implements Container<String> {
    private String[] books;
    private int count = 0;
    
    public BookShelf(int size) {
        books = new String[size];
    }
    
    public void addBook(String book) {
        books[count++] = book;
    }
    
    public Iterator<String> createIterator() {
        return new BookIterator();
    }
    
    private class BookIterator implements Iterator<String> {
        private int index = 0;
        
        public boolean hasNext() {
            return index < count;
        }
        
        public String next() {
            return books[index++];
        }
    }
}

Итератор хранит собственное состояние обхода, что позволяет нескольким итераторам обходить одну и ту же коллекцию независимо друг от друга. Клиенты используют итератор, не зная о том, как коллекция хранит свои данные:

BookShelf shelf = new BookShelf(3);
shelf.addBook("Design Patterns");
shelf.addBook("Clean Code");

Iterator<String> iterator = shelf.createIterator();
while (iterator.hasNext()) {
    System.out.println(iterator.next());
}

Встроенные в Java интерфейсы Iterable и Iterator следуют именно этому паттерну, поэтому вы можете использовать расширенные циклы for (enhanced for-loops) с любым классом, который реализует Iterable. Паттерн Итератор (Iterator Pattern) необходим, когда вам нужно предоставить несколько методов обхода или скрыть сложные внутренние структуры от клиентского кода.

challenge icon

Задание

Легко

Давайте создадим систему плейлистов, используя паттерн Итератор (Iterator Pattern)! Вы создадите музыкальный плейлист, который хранит песни и предоставляет пользовательский итератор для последовательного обхода — без раскрытия того, как песни хранятся внутри. Это идеальный вариант использования паттерна Итератор, позволяющий пользователям перемещаться по вашей коллекции через чистый, единообразный интерфейс.

Вы организуете свой код в четырех файлах:

  • Iterator.java: Определите свой универсальный интерфейс Iterator<T> с двумя методами: hasNext(), который возвращает логическое значение, указывающее, существуют ли еще элементы, и next(), который возвращает следующий элемент типа T.
  • Playlist.java: Создайте агрегирующий класс, который содержит ваши песни. Ваш Playlist должен хранить песни в массиве String с фиксированной емкостью (передаваемой в конструктор) и отслеживать количество добавленных песен. Включите метод addSong(String song) для добавления песен в плейлист.

    Вашему Playlist необходим метод createIterator(), который возвращает Iterator<String>. Реализуйте это, создав частный внутренний класс с именем PlaylistIterator, который реализует ваш интерфейс Iterator. Этот внутренний итератор поддерживает свою собственную позицию индекса и проходит по массиву песен, возвращая каждую песню по порядку.

  • Song.java: Создайте простой класс Song, который оборачивает название песни. У него должен быть конструктор, принимающий название (String), метод getTitle() и метод toString(), который возвращает "Playing: " + title.
  • Main.java: Объедините вашу систему итераторов! Вы получите один входной параметр: список названий песен, разделенных запятыми (например: Bohemian Rhapsody,Stairway to Heaven,Hotel California).

    Создайте Playlist емкостью на 10 песен. Разберите входные данные и добавьте каждое название песни в плейлист. Затем получите итератор из плейлиста и используйте его для обхода всех песен, выводя каждую из них, обернутую в объект Song (который будет отображаться как Playing: [title]).

    После итерации по всем песням выведите Playlist complete! на новой строке.

Вы получите один входной параметр: строку с названиями песен, разделенными запятыми.

Например, при вводе Yesterday,Imagine,Let It Be ваш вывод будет следующим:

Playing: Yesterday
Playing: Imagine
Playing: Let It Be
Playlist complete!

Обратите внимание, как ваш класс Main использует методы итератора hasNext() и next() для обхода плейлиста, ничего не зная о структуре базового массива. Итератор инкапсулирует всю логику обхода, скрывая внутреннее представление коллекции от клиентского кода!

Шпаргалка

Паттерн Итератор (Iterator Pattern) — это поведенческий паттерн проектирования, который предоставляет способ последовательного доступа к элементам коллекции, не раскрывая её внутреннюю структуру.

Этот паттерн отделяет логику обхода от самой коллекции, используя два основных компонента:

  • Интерфейс Iterator: определяет методы для обхода элементов
  • Iterable/Container: создает итераторы для своей коллекции

Базовая структура

interface Iterator<T> {
    boolean hasNext();
    T next();
}

interface Container<T> {
    Iterator<T> createIterator();
}

Пример реализации

class BookShelf implements Container<String> {
    private String[] books;
    private int count = 0;
    
    public BookShelf(int size) {
        books = new String[size];
    }
    
    public void addBook(String book) {
        books[count++] = book;
    }
    
    public Iterator<String> createIterator() {
        return new BookIterator();
    }
    
    private class BookIterator implements Iterator<String> {
        private int index = 0;
        
        public boolean hasNext() {
            return index < count;
        }
        
        public String next() {
            return books[index++];
        }
    }
}

Использование

BookShelf shelf = new BookShelf(3);
shelf.addBook("Design Patterns");
shelf.addBook("Clean Code");

Iterator<String> iterator = shelf.createIterator();
while (iterator.hasNext()) {
    System.out.println(iterator.next());
}

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

Попробуйте сами

import java.util.Scanner;

class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        String input = scanner.nextLine();
        
        // TODO: Создайте Playlist вместимостью 10 песен
        
        // TODO: Разберите входные данные (разделенные запятыми) и добавьте название каждой песни в playlist
        
        // TODO: Получите итератор из playlist, используя createIterator()
        
        // TODO: Используйте итератор для обхода всех песен
        // Для каждой песни оберните ее в объект Song и выведите на экран
        
        // TODO: Выведите "Playlist complete!" после обхода всех песен
    }
}
quiz iconПроверьте себя

В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.

Все уроки раздела Объектно-ориентированное программирование