Żrodło: film Terminator 2
Wzorzec, który dzisiaj omawiamy jest chyba najbardziej przydatny ze wszystkich, które do tej pory przerobiliśmy. To na nim właśnie stoi cała biblioteka Java Collections. Wzorzec Iterator jest tak niezbędny, że… aż niepotrzebny.
W artykule „SOLIDne programowanie” omawialiśmy najważniejsze zasady, które powinny przyświecać nam w czasie gdy piszemy kod. Każda z liter słowa SOLID nawiązywała do innej reguły. Tym razem skupimy się na „D” od Dependency Inversion.
Zasada odwrócenia zależności mówi nam abyśmy unikali tworzenia sztywnych połączeń opartych na konkretnych klasach:
class Example{}
class Main{
void exampleMethod(Example example){
(...)
}
}
Jeśli zrobimy to jak powyżej i nasza przykładowa metoda (exampleMethod) będzie działać tylko z tą jedną klasą Example, to sami sobie tworzymy kłopoty na przyszłość. Lepiej oprzeć parametr na abstrakcji:
interface Example{}
class Main{
void exampleMethod(Example example){
(...)
}
Zmiana mała, za to efekt ogromny – teraz klasa nie boi się rozwoju, a ponadto metoda exampleMethod nie wie nic o obiekcie który do niej trafi, czyli jednocześnie zachowujemy wysoką hermetyzację.
Wzorzec Iterator jest odpowiedzią właśnie na te dwie potrzeby:
- chcemy uzależnić metodę od abstrakcji zamiast od konkretnej klasy
- chcemy hermetyzować elementy, które mogą ulec zmianie w przyszłości
Wzorzec Iterator zapewnia metodę dostępu sekwencyjnego do elementów obiektu zagregowanego bez ujawniania jego reprezentacji wewnętrznej.
Rusz głową! Wzorce projektowe

We wstępniaku napisałem, że Iterator jest jednocześnie niezbędny i zbędny. Chodzi o to, że tak świetnie uzupełnia kolekcje, że już dawno temu został włączony do pakietu java.util, a każda klasa implementująca interfejs Collections (listy, mapy, kolejki, stosy) musi mieć metodę getIterator() która automatycznie generuje nam iterator danej kolekcji.
W związku z powyższym omawianie tego wzorca tutaj jest po trochu zbędne – szansa na to, że będziemy musieli kiedykolwiek napisać własny iterator zamiast na szybko skorzystać z gotowej metody są niewielkie.
Jednakowoż może się to zdarzyć, gdy będziemy pracować ze zwykłymi tablicami, które takiej metody nie posiadają.
Ale po kolei…
Tym razem znów nawiążemy do gier Diablopodobnych zamiast klasycznych cRPG. Wszystkie te clickery mają to do siebie, że oręż generowany jest losowo – randomizowane są statystyki, nazwy, czasem nawet wygląd. Jednocześnie mamy w grze elementy, które są stałe i za każdym razem dokładnie takie same – jak na przykład mikstury: „uleczajki„.

Mamy małą uleczajkę, która przywraca 20% maksymalnych punktów zdrowia, średnią dającą 50% i dużą, która zawsze regeneruje do pełna. To samo z miksturami przywracającymi manę, niezbędną do rzucania zaklęć. Razem dokładnie 6 pozycji.
Jeśli chciałbym to zakodować (pełna wersja tutaj) i zrobić listę gotową do zaprezentowania w sklepie z potionami to z pewnością użyłbym zwykłej tabeli, która działa szybciej od list czy setów, a ponadto zużywa mniej pamięci.
String[] elixirs = new String[6];
public ElixirStore() {
elixirs[0] = "Mała mikstura regeneracji zdrowia";
elixirs[1] = "Średnia mikstura regeneracji zdrowia";
elixirs[2] = "Duża mikstura regeneracji zdrowia";
elixirs[3] = "Mała mikstura regeneracji many";
elixirs[4] = "Średnia mikstura regeneracji many";
elixirs[5] = "Duża mikstura regeneracji many";
}
Natomiast, gdybym to samo chciał zrobić z losowo generowanym orężem to lepiej byłoby mi skorzystać choćby z ArrayList, która automatycznie się powiększa – zmieści i milion pozycji:
List<String> weapons = new ArrayList<>();
public WeaponShop() {
for (int i = 0; i < 10; i++) {
generateAndAddWeapon();
}
}
Póki co wszystko działa jak należy, problem może pojawić się gdy będziemy chcieli połączyć oba sklepy: z bronią i z miksturami. Wtedy takie centrum handlowe musiałoby mieć dwie osobne pętle:
for (String elixir : elixirStore.elixirs) {
System.out.println(elixir);
}
for (String weapon : weaponShop.weapons) {
System.out.println(weapon);
}
Na chwilę obecną nie wydaje się to krytyczną sytuacją, ale z każdym kolejnym sklepem (ze zbroją, z biżuterią, z czarami, z płaszczami, z tatuażami…) pętli będzie przybywać, a kod się będzie komplikował i zaciemniał.
Dlatego już teraz warto przemyśleć strukturę i dodać iteratory. Ten ze sklepu z bronią dostaniemy automatycznie, metodą z kolekcji:
Iterator<String> getIterator(){
return weapons.iterator();
}
Ale ten z tablicy z miksturami będziemy musieli stworzyć samodzielnie:

Wbrew pozorom nie jest to nic szczególnie skomplikowanego – ot tworzymy nową klasę, która implementuje javowy interfejs Iterator. To zmusza nas do dodania co najmniej dwóch metod: hasNext() oraz next(), które musimy zaimplementować samodzielnie. Możemy też opcjonalnie dodać funkcję remove() usuwającą element kolekcji.
I gotowe. Nasz uniwersalny sklep działa i oferuje to co trzeba. A wszystko realizujemy jedną linijką kodu, która przyjmie tyle iteratorów ile chcemy.
new GeneralStore(elixirStore.getIterator(), weaponShop.getIterator()).printOffer();

ITCandidateEvaluator
Tak jak pisałem wcześniej – własna implementacja wzorca Iterator wydaje mi się całkowicie zbędna z obecnego punktu widzenia. Nie potrafię sobie również wyobrazić sytuacji, że będę tego potrzebował w przyszłości.
Co więcej, wraz z wprowadzeniem strumieni w Java 8 iteratory straciły trochę na znaczeniu. Teraz zamiast lecieć po kolejnych elementach listy, lepiej utworzyć strumień, ustawić filtry i zebrać to na czym nam zależy.
A mi (poza oczywiście ukończeniem projektu) zależy na przećwiczeniu tychże strumieni (i lambd) z którymi wciąż miałem niewystarczający kontakt. Fajnie było się dowiedzieć na jakiej zasadzie działają iteratory, ale zostawmy tę wiedzę w szufladce otagowanej nalepką „może kiedyś” i zajmijmy się czymś ciekawszym.
Być może taki okaże się Kompozyt (Composite), którym zajmiemy się już w następnym artykule.