Паттерн «Итератор»
Часть раздела Объектно-ориентированное программирование путешествия по 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) необходим, когда вам нужно предоставить несколько методов обхода или скрыть сложные внутренние структуры от клиентского кода.
Задание
ЛегкоДавайте создадим систему плейлистов, используя паттерн Итератор (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!" после обхода всех песен
}
}
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Объектно-ориентированное программирование
1Основы ООП
Внешние файлыВведение в ООПКлассы и объектыКлючевое слово thisМетодыПоля (атрибуты)Метод-конструкторПерегрузка конструкторовИтоги — Простой калькулятор4Наследование
Основы наследования (extends)Ключевое слово superПереопределение методов (@Override)Цепочка конструкторовКласс ObjectОдиночное и многоуровневое наследованиеПочему нет множественного наследования классовИтоги — Иерархия сотрудников7Специальные методы и класс Object
Метод toString()equals() и hashCode()Метод clone()compareTo() и ComparableИнтерфейс ComparatorПовторение — Кастомная сортировка2Модификаторы доступа и инкапсуляция
Обзор уровней доступаМетоды геттеры и сеттерыСокрытие данныхКлючевое слово finalИтоги — Менеджер банковского счета5Полиморфизм
Основы перегрузки методовПереопределение методов (Run-Time)Upcasting и DowncastingОператор instanceofАбстрактные классы и методыИтоги: Калькулятор фигур8Продвинутые концепции ООП
Композиция против наследованияАгрегация против композицииВнутренние, вложенные и анонимные классыEnums и методы перечисленийRecords (Java 16+)Sealed-классы (Java 17+)11Паттерны проектирования. Часть 1
Введение в паттерны проектированияПаттерн SingletonПаттерн FactoryПаттерн BuilderПаттерн ObserverПаттерн Strategy3Свойства класса и статические члены
Переменные экземпляра и статические переменныеСтатические методыСтатические блокиКонстанты (static final)Итоги: Счетчик и утилиты6Интерфейсы и абстрактные классы
Введение в интерфейсыРеализация интерфейсовРеализация нескольких интерфейсовDefault и Static методы в интерфейсахАбстрактные классы vs ИнтерфейсыФункциональные интерфейсыИтоги — Платежная система9Обобщения
Введение в обобщенияОбобщенные классыОбобщенные методыОграниченные параметры типаСимволы подстановки (Wildcards: ?, extends, super)Итоги — Обобщенный контейнер12Паттерны проектирования. Часть 2
Паттерн «Команда»Паттерн «Адаптер»Паттерн «Декоратор»Паттерн «Шаблонный метод»Паттерн «Состояние»Паттерн «Компоновщик»Паттерн «Итератор»