Zastanawiałem się, co sprawia, że Iterator jest wyjątkowy w porównaniu z innymi podobnymi konstrukcjami, i że Gang of Four wymienia go jako wzorzec projektowy.
Iterator opiera się na polimorfizmie (hierarchii zbiorów ze wspólnym interfejsem) i oddzieleniu obaw (iteracja zbiorów powinna być niezależna od struktury danych).
Ale co, jeśli zastąpimy hierarchię zbiorów na przykład hierarchią obiektów matematycznych (liczba całkowita, liczba zmiennoprzecinkowa, liczba zespolona, macierz itp.) I iteratorem klasą reprezentującą niektóre powiązane operacje na tych obiektach, na przykład funkcje mocy. Diagram klas byłby taki sam.
Prawdopodobnie moglibyśmy znaleźć wiele innych podobnych przykładów, takich jak Writer, Painter, Encoder i prawdopodobnie lepsze, które działają w ten sam sposób. Jednak nigdy nie słyszałem, aby którykolwiek z nich nazywał się Wzorem projektowym.
Co sprawia, że Iterator jest wyjątkowy?
Czy to fakt, że jest bardziej skomplikowany, ponieważ wymaga stanu zmiennego do przechowywania bieżącej pozycji w kolekcji? Ale wtedy stan zmienności zwykle nie jest uważany za pożądany.
Aby wyjaśnić mój punkt, pozwól mi podać bardziej szczegółowy przykład.
Oto nasz problem projektowy:
Powiedzmy, że mamy hierarchię klas i operację zdefiniowaną na obiektach tych klas. Interfejs tej operacji jest taki sam dla każdej klasy, ale implementacje mogą być zupełnie inne. Zakłada się również, że sensowne jest wielokrotne zastosowanie operacji na tym samym obiekcie, powiedzmy z różnymi parametrami.
Oto rozsądne rozwiązanie naszego problemu projektowego (praktycznie uogólnienie wzoru iteratora):
W celu rozdzielenia wątpliwości implementacje operacji nie powinny być dodawane jako funkcje do oryginalnej hierarchii klas (obiekty operandów). Ponieważ chcemy zastosować tę operację wiele razy na tym samym operandzie, powinna ona być reprezentowana przez obiekt zawierający odwołanie do operandu, a nie tylko przez funkcję. Dlatego obiekt argumentu powinien zapewniać funkcję, która zwraca obiekt reprezentujący operację. Ten obiekt zapewnia funkcję, która wykonuje faktyczną operację.
Przykład:
Istnieje podstawowa klasa lub interfejs MathObject
(głupia nazwa, wiem, może ktoś ma lepszy pomysł) z klasami pochodnymi MyInteger
i MyMatrix
. Dla każdej MathObject
operacji Power
należy zdefiniować operację, która pozwala obliczyć kwadrat, sześcian i tak dalej. Więc możemy napisać (w Javie):
MathObject i = new MyInteger(5);
Power powerOfFive = i.getPower();
MyInteger square = powerOfFive.calculate(2); // should return 25
MyInteger cube = powerOfFive.calculate(3); // should return 125