Teknik Detaylar

Iterator Tasarım Deseni Nedir?

← Teknik Detaylar
2021-10-02 · 6 dk okuma
Iterator Tasarım Deseni Nedir?
Bu yazıyı yapay zekâ ile tartış
Sayfayı kopyala

Bu yazı Design Patterns/Tasarım Desenleri Nedir? başlıklı yazı dizisinin bir parçasıdır.

Bu içerik ağırlıklı olarak refactoring.guru sitesindeki içeriğin tercümesi ve derlenmesinden oluşturulmuştur.

Tüm tasarım desenleri ya da diğer adıyla tasarım kalıplarına yönelik ayrıntılı içeriklere yazının sonundaki bağlantılardan ulaşabilirsiniz.

💡 Özet (TL;DR):

  • Amaç: Bir koleksiyonun (list, tree, graph vb.) iç yapısını ve elemanları nasıl sakladığını ifşa etmeden, elemanları üzerinde sırayla dolaşmayı sağlar.
  • Kilit Yapılar: Iterator Arayüzü (Iterator), Somut Iterator (ConcreteIterator), Koleksiyon Arayüzü (IterableCollection), Somut Koleksiyon (ConcreteCollection).
  • Motto: Access elements sequentially without exposing representation (Yapıyı ifşa etmeden sırayla eriş).

Iterator Tasarım Deseninin Amacı

Iterator (Yineleyici), bir koleksiyonun (koleksiyon yapısının detaylarını bilmeden) öğelerini sırayla gezebilmenizi sağlayan davranışsal (behavioral) bir tasarım desenidir.


Sorun

Koleksiyonlar yazılımda en sık kullanılan veri yapılarıdır. Birçoğu elemanları basit bir listede (array) tutarken; bazıları yığın (stack), ağaç (tree) veya karmaşık grafik (graph) yapılarında saklayabilir.

Programlamada koleksiyonlar

Koleksiyonun yapısı ne olursa olsun, istemci kodun (client code) bu elemanlara erişebilmesi gerekir. Ancak karmaşık yapılarda elemanların üzerinden nasıl geçileceği büyük bir sorudur. Örneğin bir departman ağacında dolaşırken önce dikey mi (DFS - Depth-First Search) yoksa yatay mı (BFS - Breadth-First Search) ilerlemeliyiz?

Iterator deseni örneği

Bu dolaşım algoritmalarını doğrudan koleksiyon sınıfının içine eklemek, koleksiyonun asıl görevi olan "veri depolama" sorumluluğunun dışına çıkmasına (Single Responsibility ihlali) ve sınıfın şişmesine yol açar. Ayrıca istemci kod, koleksiyonun iç yapısına sıkı sıkıya bağımlı (tightly coupled) hale gelir.


Çözüm

Iterator deseni, koleksiyonun dolaşımla ilgili davranışlarını alarak Iterator adı verilen ayrı bir nesneye yüklemeyi önerir.

Iterator deseni nedir

Iterator nesnesi sadece dolaşım algoritmasını içermekle kalmaz; o anki pozisyon, kalan eleman sayısı gibi durum (state) bilgilerini de kendi üzerinde saklar. Bu sayede, aynı koleksiyon üzerinde birden fazla iterator bağımsız olarak ve aynı anda dolaşım yapabilir.

Genellikle tüm iteratorler next() (sonraki elemanı ver) ve hasMore() (başka eleman kaldı mı) gibi ortak bir arayüzü uygular. Böylece istemci kod, dolaştığı koleksiyonun ağaç mı, liste mi yoksa grafik mi olduğunu bilmek zorunda kalmaz.


Gerçek Hayat Senaryosu: Departman ve Çalışan Ağacı Dolaşımı

Bir şirketin departman hiyerarşisinde derinlemesine (dikey) çalışan araması yapan basit bir PHP iterasyon kurgusu:

// 1. Employee ve Collection Yapısı
class Employee {
    public string $name;
    public string $role;

    public function __construct(string $name, string $role) {
        $this->name = $name;
        $this->role = $role;
    }
}

// 2. Iterator Arayüzü
interface EmployeeIterator {
    public function getNext(): ?Employee;
    public function hasMore(): bool;
}

// 3. Somut Iterator
class DepartmentIterator implements EmployeeIterator {
    private array $employees;
    private int $position = 0;

    public function __construct(array $employees) {
        $this->employees = $employees;
    }

    public function getNext(): ?Employee {
        if ($this->hasMore()) {
            return $this->employees[$this->position++];
        }
        return null;
    }

    public function hasMore(): bool {
        return $this->position < count($this->employees);
    }
}

Iterator vs Visitor

KriterIteratorVisitor
Ana AmaçKoleksiyon elemanları üzerinde sırayla geçiş (traversal) sağlamak.Nesne yapısını değiştirmeden elemanlar üzerinde yeni işlemler gerçekleştirmek.
Durum YönetimiDolaşım durumunu (pozisyon vb.) kendi içinde saklar.Durum saklamaz, sadece elemanları ziyaret edip işlem tetikler.
Uygulama AlanıBenzer tipten oluşan veri koleksiyonlarında yaygındır.Farklı sınıflardan oluşan heterojen nesne yapılarında kullanılır.

Uygulanabilirlik

  • Karmaşık İç Yapıyı Gizlemek: Koleksiyonunuzun karmaşık bir veri yapısı varsa (ağaçlar, grafikler) ve bunu istemci koda yansıtmak istemiyorsanız kullanın.
  • Kod Tekrarını Önlemek: Farklı yerlerde tekrarlanan dolaşım (traversal) kodlarını tek bir sınıfta toplamak için kullanın.
  • Polimorfik Dolaşım: İstemci kodun farklı veri yapıları (listeler, kuyruklar, ağaçlar) üzerinde aynı arayüz ile dolaşabilmesini istediğinizde kullanın.

Diğer Tasarım Desenleri ile İlişkisi

  • Composite ağaç yapılarında dolaşmak için Iterator deseni kullanılır.
  • Koleksiyonların kendi tiplerine uygun iteratörler üretebilmesi için Factory Method deseninden yararlanılabilir.
  • Dolaşım durumunu kaydedip gerektiğinde o ana geri dönebilmek (rollback) için Memento ve Iterator birlikte kurgulanabilir.

Iterator Tasarım Deseni Kod Örnekleri

Örnek PHP Kodu

<?php

namespace RefactoringGuru\Iterator\Conceptual;

class AlphabeticalOrderIterator implements \Iterator
{
    private WordsCollection $collection;
    private int $position = 0;
    private bool $reverse = false;

    public function __construct(WordsCollection $collection, bool $reverse = false)
    {
        $this->collection = $collection;
        $this->reverse = $reverse;
    }

    public function rewind(): void
    {
        $this->position = $this->reverse ?
            count($this->collection->getItems()) - 1 : 0;
    }

    #[\ReturnTypeWillChange]
    public function current()
    {
        return $this->collection->getItems()[$this->position];
    }

    #[\ReturnTypeWillChange]
    public function key()
    {
        return $this->position;
    }

    public function next(): void
    {
        $this->position = $this->position + ($this->reverse ? -1 : 1);
    }

    public function valid(): bool
    {
        return isset($this->collection->getItems()[$this->position]);
    }
}

class WordsCollection implements \IteratorAggregate
{
    private array $items = [];

    public function getItems(): array
    {
        return $this->items;
    }

    public function addItem(string $item): void
    {
        $this->items[] = $item;
    }

    public function getIterator(): \Iterator
    {
        return new AlphabeticalOrderIterator($this);
    }

    public function getReverseIterator(): \Iterator
    {
        return new AlphabeticalOrderIterator($this, true);
    }
}

$collection = new WordsCollection();
$collection->addItem("Birinci");
$collection->addItem("İkinci");
$collection->addItem("Üçüncü");

echo "Düz Dolaşım:\n";
foreach ($collection->getIterator() as $item) {
    echo $item . "\n";
}

echo "\nTers Dolaşım:\n";
foreach ($collection->getReverseIterator() as $item) {
    echo $item . "\n";
}

Örnek Python Kodu

from __future__ import annotations
from collections.abc import Iterable, Iterator
from typing import Any, List

class AlphabeticalOrderIterator(Iterator):
    _position: int = None
    _reverse: bool = False

    def __init__(self, collection: WordsCollection, reverse: bool = False) -> None:
        self._collection = collection
        self._reverse = reverse
        self._position = -1 if reverse else 0

    def __next__(self) -> Any:
        try:
            value = self._collection[self._position]
            self._position += -1 if self._reverse else 1
        except IndexError:
            raise StopIteration()
        return value


class WordsCollection(Iterable):
    def __init__(self, collection: List[Any] = []) -> None:
        self._collection = collection

    def __iter__(self) -> AlphabeticalOrderIterator:
        return AlphabeticalOrderIterator(self._collection)

    def get_reverse_iterator(self) -> AlphabeticalOrderIterator:
        return AlphabeticalOrderIterator(self._collection, True)

    def add_item(self, item: Any) -> None:
        self._collection.append(item)


if __name__ == "__main__":
    collection = WordsCollection()
    collection.add_item("İlk")
    collection.add_item("İkinci")
    collection.add_item("Üçüncü")

    print("Düz Dolaşım:")
    for item in collection:
        print(item)
    print("")

    print("Ters Dolaşım:")
    for item in collection.get_reverse_iterator():
        print(item)

Sıkça Sorulan Sorular (FAQ)

Büyük veri kümelerinde Iterator kullanmak bellek tüketimini nasıl etkiler?

Bellek tüketimi açısından olağanüstü faydalıdır. Tüm veriyi tek seferde belleğe (RAM) yüklemek yerine, Lazy Loading (Tembel Yükleme) veya Generators (Üreticiler) kullanarak verileri ihtiyaç duyuldukça tek tek çekebilirsiniz. Bu sayede milyonlarca satırlık veriler bile minimum RAM tüketimiyle işlenebilir.

Python/JavaScript'teki yield anahtar kelimesi Iterator deseniyle mi ilişkilidir?

Evet, doğrudan dil seviyesindeki entegrasyonudur. yield anahtar kelimesi kullanan fonksiyonlar birer Generator döndürür. Bu generator'lar, fonksiyonun çalışma durumunu dondurup her adımda sıradaki değeri üreterek geri dönen hazır iteratörlerdir.

Fail-Fast vs Fail-Safe Iterator nedir?

  • Fail-Fast: Iterasyon sırasında koleksiyon üzerinde (başka bir iş parçacığı tarafından) yapısal bir değişiklik (ekleme/silme) algılandığı anda doğrudan ConcurrentModificationException hatası fırlatır. Hızlıca çökmeyi hedefler.
  • Fail-Safe: Koleksiyonun orijinali yerine, o anki bir kopyası (clone) üzerinde iterasyon yapar. Bu yüzden iterasyon sırasında koleksiyonda değişiklik yapılsa bile hata fırlatmaz ancak yapılan değişiklikler iterasyon esnasında görünmez.

Diğer Tasarım Kalıpları/Design Patterns

Oluşumsal Kalıplar (Creational Patterns)

Factory Method, Abstract Factory, Builder, Prototype, Singleton

Yapısal Kalıplar (Structural Patterns)

Adapter, Bridge, Composite, Decorator, Facade, Flyweight, Proxy

Davranışsal Kalıplar (Behavioral Patterns)

Chain of Responsibility, Command, Iterator, Mediator, Memento, Observer, State, Strategy, Template Method, Visitor