Паттерн Iterator
Часть раздела Object Oriented Programming путешествия по C на Coddy — урок 56 из 61.
Паттерн Итератор предоставляет способ последовательного доступа к элементам коллекции без раскрытия её внутренней структуры. Вместо того чтобы предоставлять пользователям прямой доступ к массиву, вы даете им объект, который умеет перебирать данные по одному элементу за раз.
Итератору обычно требуются две части информации: ссылка на коллекцию и текущая позиция. В языке C мы создаем структуру (struct) для хранения этого состояния:
typedef struct {
int* data; // указатель на массив
int size; // общее количество элементов
int current; // текущая позиция
} IntIterator;Итератор предоставляет две ключевые функции. has_next() проверяет, есть ли еще элементы для чтения, а next() возвращает текущий элемент и перемещает позицию вперед:
int has_next(IntIterator* it) {
return it->current < it->size;
}
int next(IntIterator* it) {
return it->data[it->current++];
}Использование итератора выглядит лаконично и скрывает детали массива:
IntIterator it = create_iterator(numbers, 5);
while (has_next(&it)) {
printf("%d\n", next(&it));
}Вызывающей стороне никогда не нужно знать, что numbers является массивом или как работает индексация. Эта абстракция позволяет легко изменить базовую структуру данных позже — например, с массива на связный список — без изменения кода, который использует итератор.
Задание
ЛегкоДавайте создадим итератор NumberList — чистую абстракцию, которая позволяет обходить коллекцию целых чисел, не раскрывая структуру базового массива.
Вы организуете свой код в трех файлах:
iterator.h: Определите структуруIntIterator, которая содержит указатель на массив целых чисел, общий размер коллекции и текущую позицию. Объявите три функции:create_iterator(принимает указатель на массив и размер, возвращает инициализированный итератор),has_next(проверяет, остались ли еще элементы) иnext(возвращает текущий элемент и продвигает позицию вперед). Не забудьте про include guards!iterator.c: Реализуйте функции итератора. Функцияcreate_iteratorдолжна возвращатьIntIteratorпо значению с текущей позицией, установленной в 0. Функцияhas_nextвозвращает 1, если есть еще элементы для чтения, и 0 в противном случае. Функцияnextвозвращает элемент в текущей позиции, а затем увеличивает позицию.main.c: Считайте количество элементов, затем считайте каждое целое число в массив. Создайте итератор для этого массива, затем используйте цикл while с функциямиhas_nextиnext, чтобы обойти и вывести каждый элемент на новой строке.
Ваша программа получит:
- Количество элементов в массиве
- Каждое целое число на отдельной строке
Используйте паттерн «Итератор» для вывода всех элементов — никакого прямого индексирования массива в цикле обхода!
Пример вывода, когда входные данные 4, затем 10, 20, 30, 40:
10
20
30
40Пример вывода, когда входные данные 3, затем -5, 0, 100:
-5
0
100Пример вывода, когда входные данные 1, затем 42:
42Итератор скрывает то, как хранятся данные — ваш основной цикл просто спрашивает «есть ли еще?» и «дай мне следующий», ничего не зная об индексах массива или расположении в памяти.
Шпаргалка
Паттерн Итератор предоставляет способ последовательного доступа к элементам коллекции, не раскрывая её внутреннюю структуру.
Структура итератора хранит ссылку на коллекцию, размер и текущую позицию:
typedef struct {
int* data; // указатель на массив
int size; // общее количество элементов
int current; // текущая позиция
} IntIterator;Итератор предоставляет две ключевые функции:
int has_next(IntIterator* it) {
return it->current < it->size;
}
int next(IntIterator* it) {
return it->data[it->current++];
}Использование итератора скрывает детали реализации массива:
IntIterator it = create_iterator(numbers, 5);
while (has_next(&it)) {
printf("%d\n", next(&it));
}Эта абстракция позволяет изменять базовую структуру данных без модификации клиентского кода.
Попробуйте сами
#include <stdio.h>
#include "iterator.h"
int main() {
int n;
scanf("%d", &n);
int arr[n];
// TODO: Считайте n целых чисел в массив
for (int i = 0; i < n; i++) {
scanf("%d", &arr[i]);
}
// TODO: Создайте итератор для массива
// TODO: Используйте цикл while с has_next и next для обхода
// и выведите каждый элемент на отдельной строке
// НЕ используйте прямую индексацию массива в цикле обхода!
return 0;
}
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Object Oriented Programming
1Основы модульного программирования
Заголовочные файлыСтражи включенияИсходные файлыСтатические функцииПовторение: Модульный калькулятор4Инкапсуляция
Концепция непрозрачных указателейОпределение непрозрачных структурГеттеры и сеттерыВалидация в сеттерахИтоги: Секретный ящик2Объекты и методы
Структуры как объектыУказатель 'Self'Константная корректностьУказатель против значенияВспомогательные методыИтоги: Point Manager5Проект: Простой банковский счет
Настройка проектаРеализация счета3Жизненный цикл объекта
Паттерн «Конструктор»Паттерн «Деструктор»Инициализация в стекеГлубокое копированиеПовторение: String Wrapper6Наследование через композицию
Встраивание структурПравило первого элементаДоступ к элементам родителяUpcastingПовторение: Иерархия фигур9Проект: Рисование фигур
Обзор проектаРеализация круга