Паттерн Iterator (Итератор) предоставляет способ последовательного доступа к элементам коллекции без раскрытия её внутренней структуры. Он выносит логику обхода из самой коллекции, обеспечивая единый интерфейс для перебора различных структур данных.
Когда применять Iterator
- Когда нужно пройти по элементам коллекции, не раскрывая её внутреннего устройства.
- Когда требуется иметь несколько независимых итераторов для одной и той же коллекции.
- Когда необходимо реализовать собственные способы обхода элементов (например, в обратном порядке).
PlantUML-диаграмма
// Интерфейс итератора
public interface IIterator<T>
{
bool HasNext();
T Next();
}
// Интерфейс коллекции
public interface IAggregate<T>
{
IIterator<T> CreateIterator();
}
// Конкретная коллекция
public class WordsCollection : IAggregate<string>
{
private List<string> _items = new List<string>();
public void AddItem(string item) => _items.Add(item);
public IIterator<string> CreateIterator()
{
return new AlphabeticalIterator(this);
}
public int Count => _items.Count;
public string this[int index] => _items[index];
}
// Конкретный итератор
public class AlphabeticalIterator : IIterator<string>
{
private WordsCollection _collection;
private int _position = -1;
public AlphabeticalIterator(WordsCollection collection)
{
_collection = collection;
}
public bool HasNext()
{
return _position < _collection.Count - 1;
}
public string Next()
{
_position++;
return _collection[_position];
}
}
// Клиентский код
class Program
{
static void Main()
{
var collection = new WordsCollection();
collection.AddItem("Банан");
collection.AddItem("Яблоко");
collection.AddItem("Апельсин");
var iterator = collection.CreateIterator();
Console.WriteLine("Перебор элементов коллекции:");
while (iterator.HasNext())
{
Console.WriteLine(iterator.Next());
}
}
}
// В Go для перебора хватает range, но паттерн виден,
// когда порядок обхода задаёт сама коллекция.
type Iterator[T any] interface {
HasNext() bool
Next() T
}
type Aggregate[T any] interface {
CreateIterator() Iterator[T]
}
type WordsCollection struct {
items []string
}
func (c *WordsCollection) AddItem(item string) {
c.items = append(c.items, item)
}
func (c *WordsCollection) CreateIterator() Iterator[string] {
return &AlphabeticalIterator{collection: c, position: -1}
}
type AlphabeticalIterator struct {
collection *WordsCollection
position int
}
func (i *AlphabeticalIterator) HasNext() bool {
return i.position < len(i.collection.items)-1
}
func (i *AlphabeticalIterator) Next() string {
i.position++
return i.collection.items[i.position]
}
func main() {
collection := &WordsCollection{}
collection.AddItem("Банан")
collection.AddItem("Яблоко")
collection.AddItem("Апельсин")
iterator := collection.CreateIterator()
fmt.Println("Перебор элементов коллекции:")
for iterator.HasNext() {
fmt.Println(iterator.Next())
}
}
from typing import Iterator, Protocol
class Aggregate(Protocol):
def create_iterator(self) -> Iterator[str]: ...
class WordsCollection:
def __init__(self) -> None:
self._items: list[str] = []
def add_item(self, item: str) -> None:
self._items.append(item)
# Итератор в Python — часть языка: генератор уже реализует протокол,
# отдельный класс с has_next/next писать не нужно.
def create_iterator(self) -> Iterator[str]:
yield from self._items
collection = WordsCollection()
collection.add_item("Банан")
collection.add_item("Яблоко")
collection.add_item("Апельсин")
print("Перебор элементов коллекции:")
for word in collection.create_iterator():
print(word)
class WordsCollection {
private readonly items: string[] = [];
addItem(item: string): void {
this.items.push(item);
}
// Протокол итератора встроен в язык: достаточно генератора,
// и коллекция сразу работает в for...of.
*[Symbol.iterator](): Iterator<string> {
yield* this.items;
}
}
const collection = new WordsCollection();
collection.addItem("Банан");
collection.addItem("Яблоко");
collection.addItem("Апельсин");
console.log("Перебор элементов коллекции:");
for (const word of collection) {
console.log(word);
}
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
// Iterable/Iterator есть в стандартной библиотеке — свой интерфейс не нужен.
class WordsCollection implements Iterable<String> {
private final List<String> items = new ArrayList<>();
void addItem(String item) {
items.add(item);
}
@Override
public Iterator<String> iterator() {
return new Iterator<>() {
private int position = 0;
public boolean hasNext() {
return position < items.size();
}
public String next() {
return items.get(position++);
}
};
}
}
public class Program {
public static void main(String[] args) {
WordsCollection collection = new WordsCollection();
collection.addItem("Банан");
collection.addItem("Яблоко");
collection.addItem("Апельсин");
System.out.println("Перебор элементов коллекции:");
for (String word : collection) {
System.out.println(word);
}
}
}
class WordsCollection : Iterable<String> {
private val items = mutableListOf<String>()
fun addItem(item: String) {
items += item
}
// Iterable — часть стандартной библиотеки, свой интерфейс не нужен.
override fun iterator(): Iterator<String> = items.iterator()
}
fun main() {
val collection = WordsCollection().apply {
addItem("Банан")
addItem("Яблоко")
addItem("Апельсин")
}
println("Перебор элементов коллекции:")
collection.forEach { println(it) }
}
Плюсы и минусы
| Плюсы | Минусы |
|---|---|
| Упрощает обход коллекций без знания их структуры | Увеличивает количество классов |
| Позволяет реализовать разные способы обхода | Может быть избыточен для простых коллекций |
| Следует принципу единственной ответственности | Итератор может нарушить инкапсуляцию коллекции |
Заключение
Iterator — поведенческий паттерн, который делает коллекции более гибкими и универсальными. Он используется повсеместно — от стандартных коллекций .NET до собственных структур данных, позволяя создавать единообразные способы обхода элементов без зависимости от их внутренней реализации.