Wzorzec projektowy Iterator

Cel

Rożnorodność kolekcji obiektowych (listy, kolejki, stosy, zbiory, multizbiory, mapy etc.), zarówno funkcjonalna, jak i implementacyjna, powoduje, że wiele z nich wymaga specyficznej obsługi i stosowania zrożnicowanych metod dostępu do elementow. Wzorzec Iterator odpowiada na potrzebę zunifikowanego dostępu do elementow kolekcji, ktory pozwoli pominąć rożnice w ich implementacji. Dzięki niemu, niezależnie od rodzaju kolekcji, jej elementy mogą być przetwarzane sekwencyjnie, z zachowaniem własności poszczegolnych kolekcji.

Struktura

Wzorzec Iterator składa się z dwoch klas abstrakcyjnych: Aggregate i Iterator, oraz dwoch klas konkretnych: ConcreteAggregate i ConcreteIterator. Wszystkie kolekcje są implementacją interfejsu Aggregate, tzn. posiadają metodę tworzącą iterator. Iterator, podobnie jak Aggregate, jest jedynie specyfikacją interfejsu, jaki każdy iterator musi posiadać. Klient, odwołując się do metody createIterator() w kolekcji, otrzymuje klasę implementującą interfejs Iterator. Dzięki temu klient nie zna konkretnej klasy implementacyjnej, a jedynie interfejs, do ktorego musi się odwoływać. Taka sytuacja ma miejsce np. w bibliotece Java Collections: każda kolekcji tworzy swoj własny iterator, ktory jednak jest dostępny wyłącznie poprzez wspolny interfejs Iterator. W ten sposob mogą one być traktowane w jednolity sposob. Iterator posiada wewnętrzny wskaźnik, ktory wskazuje na aktualny element kolekcji. Iteratory definiują podstawowe operacje pozwalające na sekwencyjny dostęp do wszystkich elementow dowolnej kolekcji: getFirst() - ustawiająca wskaźnik iteratora na początek kolekcji, getNext() - zwracająca kolejny element, hasNext() - sprawdzająca, czy kolejny element istnieje. W niektorych implementacjach iterator pozwala także na modyfikacje kolekcji, np. dodawanie i usuwanie elementow. Kolekcje o specyficznej strukturze, np. listy mogą udostępniać itera tory wykorzystujące wiedzę o tej strukturze, np. udostępniającą możliwość swobodnego dostępu do elementow kolekcji, zmiany kierunku trawersu kolekcji etc. Z uwagi na konieczność dostępu do elementow kolekcji, iterator musi posiadać prawo odwołania się do nich. W praktyce jest on zatem zwykle klasą zaprzyjaźnioną lub wewnętrzną kolekcji.

Uczestnicy

We wzorcu uczestniczą dwie hierarchie obiektow: związanych z kolekcjami (Aggregate i jej klasy potomne) i związanych z iteracją (Iterator i jego podklasy). Obie hierarchie są powiązane ze sobą wyłącznie poprzez interfejsy. Warto zwrocić uwagę, że struktura wzorca i role pełnione przez poszczegolne klasy są szczegolnym przypadkiem struktury i rol zdefiniowanych we wzoru Factory Method. Tam rownież klient odwołuje się do abstrakcyjnej metody klasy-fabryki w celu otrzymania abstrakcyjnego produktu, a faktycznie wywołuje metody w implementacji klasy-fabryki i otrzymuje konkretny produkt zależny od użytej fabryki.

Konsekwencje

Iterator pozwala na oddzielenie kolekcji, czyli klasy związanej z przechowywaniem obiektow, od mechanizmu dostępu do tych obiektow. Dzięki temu klient odwołuje się do obiektow w sposob abstrakcyjny, niezależny od konkretnej implementacji kolekcji. Konstrukcja iteratora pozwala na jednoczesne wspołistnienie wielu niezależnych iteratorow, ponieważ każdy przechowuje wewnętrznie wskaźnik do aktualnie wskazywanego obiektu w kolekcji. Niektore kolekcje mogą definiować kilka rożnych iteratorow, o zrożnicowanej funkcjonalności (w przypadku np. listy).