Iteratormuster
Teil des Abschnitts Objektorientierte Programmierung der Java-Journey von Coddy. Lektion 76 von 87.
Das Iterator Pattern ist ein Verhaltensentwurfsmuster, das eine Möglichkeit bietet, auf die Elemente einer Sammlung nacheinander zuzugreifen, ohne ihre zugrunde liegende Struktur offenzulegen. Unabhängig davon, ob du mit einem Array, einer verketteten Liste oder einem Baum arbeitest, bietet dir der Iterator eine einheitliche Möglichkeit, die Elemente zu durchlaufen.
Das Muster trennt die Traversierungslogik von der Sammlung selbst. Es umfasst zwei Hauptkomponenten: eine Iterator-Schnittstelle, die Methoden zum Durchlaufen von Elementen definiert, und ein Iterable (oder Aggregate), das Iteratoren für seine Sammlung erstellt:
interface Iterator<T> {
boolean hasNext();
T next();
}
interface Container<T> {
Iterator<T> createIterator();
}
class BookShelf implements Container<String> {
private String[] books;
private int count = 0;
public BookShelf(int size) {
books = new String[size];
}
public void addBook(String book) {
books[count++] = book;
}
public Iterator<String> createIterator() {
return new BookIterator();
}
private class BookIterator implements Iterator<String> {
private int index = 0;
public boolean hasNext() {
return index < count;
}
public String next() {
return books[index++];
}
}
}Der Iterator verwaltet seinen eigenen Traversierungszustand, sodass mehrere Iteratoren dieselbe Sammlung unabhängig voneinander durchlaufen können. Clients verwenden den Iterator, ohne zu wissen, wie die Sammlung ihre Daten speichert:
BookShelf shelf = new BookShelf(3);
shelf.addBook("Design Patterns");
shelf.addBook("Clean Code");
Iterator<String> iterator = shelf.createIterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}Javas integrierte Schnittstellen Iterable und Iterator folgen genau diesem Muster. Deshalb kannst du erweiterte for-Schleifen mit jeder Klasse verwenden, die Iterable implementiert. Das Iterator-Muster ist unverzichtbar, wenn du mehrere Traversierungsmethoden bereitstellen oder komplexe interne Strukturen vor dem Client-Code verbergen musst.
Aufgabe
EinfachErstellen wir ein Playlist-System mit dem Iterator Pattern! Du erstellst eine Musik-Playlist, die Songs speichert und einen benutzerdefinierten Iterator bereitstellt, um sie sequenziell zu durchlaufen, ohne offenzulegen, wie die Songs intern gespeichert werden. Dies ist ein perfekter Anwendungsfall für das Iterator Pattern, mit dem Benutzer deine Sammlung über eine klare, einheitliche Schnittstelle durchsuchen können.
Du organisierst deinen Code auf vier Dateien:
Iterator.java: Definiere deine generischeIterator<T>-Schnittstelle mit zwei Methoden:hasNext(), die einen booleschen Wert zurückgibt, der angibt, ob weitere Elemente vorhanden sind, undnext(), die das nächste Element vom Typ T zurückgibt.Playlist.java: Erstelle die Aggregatklasse, die deine Songs enthält. DeinePlaylistsollte Songs in einem String-Array mit einer festen Kapazität speichern (die an den Konstruktor übergeben wird) und verfolgen, wie viele Songs hinzugefügt wurden. Füge eineaddSong(String song)-Methode hinzu, um Songs zur Playlist hinzuzufügen.Deine Playlist benötigt eine
createIterator()-Methode, die einenIterator<String>zurückgibt. Implementiere dies, indem du eine private innere Klasse namensPlaylistIteratorerstellst, die deine Iterator-Schnittstelle implementiert. Dieser innere Iterator verwaltet seine eigene Indexposition und durchläuft das Songs-Array, wobei er jeden Song in der richtigen Reihenfolge zurückgibt.Song.java: Erstelle eine einfacheSong-Klasse, die einen Songtitel kapselt. Sie sollte einen Konstruktor haben, der den Titel (String) entgegennimmt, sowie einegetTitle()-Methode und einetoString()-Methode, diePlaying: [title]zurückgibt.Main.java: Führe dein Iterator-System zusammen! Du erhältst eine Eingabe: eine durch Kommas getrennte Liste von Songtiteln (zum Beispiel:Bohemian Rhapsody,Stairway to Heaven,Hotel California).Erstelle eine Playlist mit einer Kapazität für 10 Songs. Verarbeite die Eingabe und füge jeden Songtitel zur Playlist hinzu. Rufe anschließend einen Iterator von der Playlist ab und verwende ihn, um alle Songs zu durchlaufen. Gib dabei jeden Song in einem Song-Objekt gekapselt aus (das als
Playing: [title]angezeigt wird).Nachdem alle Songs durchlaufen wurden, gib
Playlist complete!in einer neuen Zeile aus.
Du erhältst eine Eingabe: eine durch Kommas getrennte Zeichenkette mit Songtiteln.
Beispielsweise würde deine Ausgabe bei der Eingabe Yesterday,Imagine,Let It Be folgendermaßen aussehen:
Playing: Yesterday
Playing: Imagine
Playing: Let It Be
Playlist complete!Beachte, wie deine Main-Klasse die Methoden hasNext() und next() des Iterators verwendet, um die Playlist zu durchlaufen, ohne etwas über die zugrunde liegende Array-Struktur zu wissen. Der Iterator kapselt die gesamte Logik zum Durchlaufen und hält die interne Darstellung der Sammlung vor dem Client-Code verborgen!
Probier es selbst
import java.util.Scanner;
class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
String input = scanner.nextLine();
// TODO: Erstelle eine Playlist mit Kapazität für 10 Songs
// TODO: Analysiere die Eingabe (kommagetrennt) und füge jeden Songtitel zur Playlist hinzu
// TODO: Erhalte einen Iterator von der Playlist mit createIterator()
// TODO: Verwende den Iterator, um alle Songs zu durchlaufen
// Für jeden Song, verpacke ihn in ein Song-Objekt und gib ihn aus
// TODO: Print "Playlist complete!" after iterating through all songs
}
}
Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.
Alle Lektionen in Objektorientierte Programmierung
1Grundlagen der OOP
Externe DateienEinführung in die OOPKlassen vs. ObjekteDas Schlüsselwort thisMethodenFelder (Attribute)KonstruktormethodeÜberladen von KonstruktorenRückblick – Einfacher Taschenrechner4Vererbung
Grundlegende Vererbung (extends)Das Schlüsselwort superÜberschreiben von Methoden (@Override)KonstruktorverkettungDie Object-KlasseEinfach- und mehrstufige VererbungWarum keine Mehrfachvererbung von KlassenRückblick – Mitarbeiterhierarchie7Spezielle Methoden & Object-Klasse
toString()-Methodeequals() und hashCode()clone()-MethodecompareTo() und ComparableComparator-SchnittstelleRückblick – Benutzerdefinierte Sortierung2Zugriffsmodifikatoren & Kapselung
Übersicht der ZugriffsebenenGetter- und Setter-MethodenInformationsverbergungDas Schlüsselwort finalRückblick – Bankkontoverwaltung5Polymorphismus
Grundlagen der MethodenüberladungMethodenüberschreibung (Laufzeit)Upcasting und DowncastingDer instanceof-OperatorAbstrakte Klassen und MethodenRückblick – Formrechner8Fortgeschrittene OOP-Konzepte
Komposition vs. VererbungAggregation vs. KompositionInnere, verschachtelte & anonyme KlassenEnums und Enum-MethodenRecords (Java 16+)Versiegelte Klassen (Java 17+)11Entwurfsmuster Teil 1
Einführung in EntwurfsmusterSingleton-MusterFactory-MusterBuilder-MusterObserver-MusterStrategie-Muster14Abschließende Herausforderungen
E-Learning-PlattformBankingsystemEntwicklung einer SpielfigurFahrzeugvermietungsdienst3Klassen-Props & statische Member
Instanz- vs. statische VariablenStatische MethodenStatische BlöckeKonstanten (static final)Wiederholung – Counter & Utility6Schnittstellen & abstrakte Klassen
Einführung in InterfacesInterfaces implementierenMehrere Interfaces implementierenDefault & Static in InterfacesAbstrakte Klassen vs. InterfacesFunktionale InterfacesRückblick – Zahlungssystem9Generics
Einführung in GenericsGenerische KlassenGenerische MethodenGebundene TypparameterWildcards (?, extends, super)Rückblick – Generischer Container12Entwurfsmuster Teil 2
BefehlsmusterAdaptermusterDekorierermusterSchablonenmethodenmusterZustandsmusterKompositmusterIteratormusterÜbe selbstständig: Online-Java-Compiler