Das Iterator Pattern ist ein Verhaltensmuster, das es ermöglicht, Elemente einer Sammlung sequentiell zu durchlaufen, ohne die zugrunde liegende Struktur der Sammlung zu kennen. Es trennt die Logik des Zugriffs auf Elemente von der Sammlung selbst. Auf diese Weise können verschiedene Arten von Sammlungen auf gleiche Weise durchlaufen werden.
Was ist das Iterator Pattern?
Das Iterator Pattern definiert eine Möglichkeit, auf Elemente einer Sammlung zuzugreifen, ohne die interne Struktur der Sammlung zu kennen. Anstatt die Sammlung selbst zu ändern, ermöglicht das Muster das Durchlaufen der Sammlung mit einem externen Iterator. Dadurch bleibt die Sammlung flexibel und unabhängig von den Details des Zugriffs.
Komponenten des Iterator Patterns
Das Iterator Pattern besteht aus mehreren wichtigen Komponenten:
- Iterator: Ein Interface, das die Methoden
hasNext()
undnext()
definiert. Es gibt an, ob noch ein weiteres Element vorhanden ist und gibt das nächste Element zurück. - ConcreteIterator: Eine konkrete Implementierung des Iterators, die auf eine Sammlung zugreift und die tatsächliche Navigation ermöglicht.
- Aggregate: Ein Interface, das eine Methode zur Erstellung eines Iterators definiert. Die Sammlung stellt einen Iterator zur Verfügung, um auf ihre Elemente zuzugreifen.
- ConcreteAggregate: Eine konkrete Implementierung der Sammlung, die den Iterator zurückgibt und die Sammlung von Elementen enthält.
Beispiel des Iterator Patterns in C++
Um das Iterator Pattern zu veranschaulichen, erstellen wir ein Beispiel, bei dem wir eine Sammlung von Zahlen durchlaufen. Dazu definieren wir eine Sammlung und einen Iterator, um auf deren Elemente zuzugreifen.
#include <iostream>
#include <vector>
#include <memory>
// Iterator: Schnittstelle für den Iterator
class Iterator {
public:
virtual bool hasNext() = 0;
virtual int next() = 0;
virtual ~Iterator() = default;
};
// Aggregate: Schnittstelle für die Sammlung
class Aggregate {
public:
virtual std::shared_ptr<Iterator> createIterator() = 0;
virtual ~Aggregate() = default;
};
// ConcreteIterator: Konkrete Implementierung des Iterators
class ConcreteIterator : public Iterator {
private:
std::vector<int>& collection;
size_t index;
public:
ConcreteIterator(std::vector<int>& collection)
: collection(collection), index(0) {}
bool hasNext() override {
return index < collection.size();
}
int next() override {
return collection[index++];
}
};
// ConcreteAggregate: Konkrete Implementierung der Sammlung
class ConcreteAggregate : public Aggregate {
private:
std::vector<int> items;
public:
ConcreteAggregate(std::initializer_list<int> list) : items(list) {}
std::shared_ptr<Iterator> createIterator() override {
return std::make_shared<ConcreteIterator>(items);
}
};
// Client-Code
int main() {
ConcreteAggregate collection = {1, 2, 3, 4, 5};
std::shared_ptr<Iterator> iterator = collection.createIterator();
while (iterator->hasNext()) {
std::cout << "Element: " << iterator->next() << std::endl;
}
return 0;
}
Erklärung des C++-Beispiels
- Iterator (Schnittstelle): Die Schnittstelle
Iterator
definiert zwei wichtige Methoden:hasNext()
prüft, ob noch ein weiteres Element in der Sammlung vorhanden ist, undnext()
gibt das nächste Element zurück. - ConcreteIterator (Konkreter Iterator): Die Klasse
ConcreteIterator
implementiert dasIterator
-Interface. Sie enthält einen Verweis auf die Sammlung und ein Indexfeld, das den aktuellen Position im Array verfolgt. Die MethodehasNext()
prüft, ob noch Elemente vorhanden sind, undnext()
gibt das nächste Element zurück. - Aggregate (Schnittstelle): Das
Aggregate
-Interface definiert die MethodecreateIterator()
. Diese Methode stellt einen Iterator zur Verfügung, der die Sammlung durchläuft. - ConcreteAggregate (Konkrete Sammlung): Die Klasse
ConcreteAggregate
implementiert dasAggregate
-Interface und speichert die Elemente in einemstd::vector
. Sie erstellt einen konkreten Iterator, um auf ihre Elemente zuzugreifen. - Client (Benutzer): Der Client erstellt eine Sammlung von Zahlen und verwendet den Iterator, um die Elemente durchzugehen. Der Client muss sich nicht um die interne Struktur der Sammlung kümmern.
Vorteile des Iterator Patterns
- Trennung von Sammlung und Zugriff: Der Iterator trennt die Logik des Zugriffs auf Elemente von der Sammlung selbst. Dies erleichtert die Wartung und Flexibilität.
- Wiederverwendbarkeit: Der gleiche Iterator kann für verschiedene Sammlungen verwendet werden, die das Iterator-Interface implementieren.
- Erweiterbarkeit: Neue Sammlungen können hinzugefügt werden, ohne den Iterator zu ändern. Ein neuer Iterator kann einfach für jede Sammlung erstellt werden.
- Verbergen der Implementierung: Der Iterator verbirgt die interne Struktur der Sammlung. Der Benutzer muss nicht wissen, wie die Sammlung organisiert ist.
Nachteile des Iterator Patterns
- Komplexität: Das Hinzufügen von Iteratoren für jede Sammlung kann zu einer höheren Komplexität führen, besonders bei einfachen Sammlungen.
- Speicherverbrauch: Da jeder Iterator eine eigene Instanz benötigt, kann der Speicherverbrauch steigen, insbesondere bei großen Sammlungen.
- Leistungseinbußen: Iteratoren können bei sehr komplexen Sammlungen oder verschachtelten Strukturen zu Leistungsproblemen führen.
Fazit
Das Iterator Pattern ist ein nützliches Muster, um den Zugriff auf Elemente einer Sammlung zu abstrahieren. Es trennt die Logik des Zugriffs von der Sammlung selbst und ermöglicht es, verschiedene Sammlungen auf gleiche Weise zu durchlaufen. Das Beispiel in C++ zeigt, wie einfach das Muster zu implementieren ist und wie es die Flexibilität erhöht. Das Iterator Pattern bietet eine saubere und wartbare Möglichkeit, mit Sammlungen zu arbeiten, ohne sich um deren interne Struktur kümmern zu müssen.
Zurück zur Liste der Pattern: Liste der Design-Pattern