Menu
Coddy logo textTech

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

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

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

Шаблон отделяет логику обхода от самой коллекции. Он включает два основных компонента: интерфейс 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++];
        }
    }
}

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

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. Паттерн Iterator необходим, когда вам нужно предоставить несколько способов обхода или скрыть сложные внутренние структуры от клиентского кода.

challenge icon

Задание

Легко

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

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

  • Iterator.java: Define свой generic интерфейс Iterator<T> с двумя методами: hasNext(), который возвращает boolean, указывающий, существуют ли еще элементы, и next(), который возвращает следующий элемент типа T.
  • Playlist.java: Create aggregate class, которая holds песни. Твой Playlist должен хранить песни в массиве String с фиксированной capacity (переданной в constructor) и отслеживать, сколько песен было added. Добавь метод addSong(String song) для добавления песен в плейлист.

    Твоему Playlist нужен метод createIterator(), который возвращает Iterator<String>. Implement это, создав private inner class с именем PlaylistIterator, который implements твой интерфейс Iterator. Этот inner iterator хранит собственную позицию index и выполняет перебор массива песен, возвращая каждую песню по порядку.

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

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

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

Ты получишь один input: строку с названиями песен, разделенными запятыми.

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

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

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

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