Menu
CoddyTech

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

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

Паттерн «Итератор» — это поведенческий шаблон проектирования, который позволяет последовательно получать доступ к элементам коллекции, не раскрывая её внутреннюю структуру. Работаете ли вы с массивом, связным списком или деревом, итератор предоставляет единый способ обхода элементов.

Этот шаблон отделяет логику обхода от самой коллекции. Он включает два основных компонента: интерфейс 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 можно использовать с любым классом, реализующим Iterable. Шаблон «Итератор» необходим, когда нужно предоставить несколько способов обхода или скрыть сложные внутренние структуры от клиентского кода.

challenge icon

Задание

Легко

Давай создадим систему плейлистов с помощью шаблона «Итератор»! Ты создашь музыкальный плейлист, который хранит песни и предоставляет собственный итератор для последовательного перебора песен, не раскрывая способ их внутреннего хранения. Это идеальный пример использования шаблона «Итератор»: он позволяет пользователям просматривать коллекцию через единый и понятный интерфейс.

Ты распределишь код по четырём файлам:

  • 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(), чтобы перебирать плейлист, не зная ничего об устройстве базового массива. Итератор инкапсулирует всю логику перебора, скрывая внутреннее представление коллекции от клиентского кода!

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

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: Разберите входную строку (значения разделены запятыми) и добавьте название каждой песни в плейлист
        
        // TODO: Получите итератор плейлиста с помощью createIterator()
        
        // TODO: Используйте итератор, чтобы перебрать все песни
        // Для каждой песни создайте объект Song и выведите его
        
        // TODO: Выведите "Playlist complete!" после перебора всех песен
    }
}
quiz iconПроверьте себя

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

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

Потренируйтесь самостоятельно: Онлайн-компилятор Java