Разбор Паттерна «Итератор»
06 11 2021 admin Пока нет комментариевТип: поведенческий.
Назначение:
Позволяет последовательно перебирать элементы составного объекта (коллекции, источника данных) без раскрытия его внутреннего представления.
Схема(refactoring.guru):
На мой взгляд, паттерн очень хорошо описан в «Банде Четырех». Лишь подмечу некоторые моменты своими словами для лучшего понимания.
СТРУКТУРА КОДА ПРИ ИСПОЛЬЗОВАНИИ ШАБЛОНА
- Есть некий составной объект. В простейшем случае можно представить себе реальную коллекцию(имею ввиду класс) вроде eloquent из laravel, где объекты уже сформированы и лежат в памяти, но это не обязательно так. Я встречал и другие примеры, по сути здесь может быть любой источник данных — внешнее api, redis, база данных. В таком случае коллекция объектов ещё не собрана, но концептуально можно себе представить базу данных как коллекцию записей. Этакая, виртуальная коллекция.
- Клиентский код, которому нужно последовательно(важно!) получать элементы из этой коллекции и при этом не зависеть от её реализации. Использует метод коллекции createIterator(), полученный итератор и его элементы.
- Есть класс-итератор, который берет на себя обязанность по перебору коллекции из п.1(то есть знает алгоритм перебора) и во вне доступен только по интерфейсу «iterator».
- Есть интерфейс «IterableCollection», который нужен для того, чтобы клиентский код мог получить доступ к итератору и зависеть только от интерфейса.
- Коллекция реализует интерфейс «IterableCollection», у неё появляется метод для доступа к итератору.
В каких условиях выгодно применять
- Обязательное, но недостаточное условие применения паттерна — вызывающему коду необходимо последовательно перебирать объекты коллекции. По моему опыту, операции, где критически важен именно последовательный перебор, достаточно редкие. Признак такой ситуации — использование генераторов в PHP. И именно в таких условиях можно задуматься про iterator. Иначе проще получить из коллекции выборку нужных элементов и работать с ней. Например, при помощи отдельных методов коллекции, или же, чтоб не размывать ответственность класса, вынести выборку в отдельный класс. На мой взгляд, так логичнее и нагляднее, вместо вызова метода $collection->createRedElementsIterator():iterable, запросить у коллекции метод $collection->getRedElements():array.
- Не хотим раскрывать алгоритм перебора элементов коллекции. Например, там сложная логика.
- Не хотим размывать ответственность коллекции всевозможными выборками(особенно, если их много).
- Клиентский код вынужден работать с несколькими коллекциями и в нём есть общая механика работы с элементами каждой коллекций. В этом случае итератор можно использовать для создания единого интерфейса обхода элементов.
Из книги:
5. поддержка нескольких активных обходов одного и того же агрегирован-
ного объекта
Это важная часть паттерна, но я затрудняюсь найти ей применение в PHP. Это может пригодиться при параллельном переборе массива, но в PHP нет параллелизма из коробки, на практике для этого используют очереди, а каждый таск очереди и так представляет отдельный процесс со своим курсором.
Паттерн интересный, но конкретно в PHP, я нахожу не так много условий для его использования.