Wzorzec iteratora
Część sekcji Programowanie obiektowe ścieżki Java w Coddy. Lekcja 76 z 87.
Wzorzec iteratora to behawioralny wzorzec projektowy, który umożliwia sekwencyjny dostęp do elementów kolekcji bez ujawniania jej wewnętrznej struktury. Niezależnie od tego, czy pracujesz z tablicą, listą połączoną czy drzewem, iterator zapewnia jednolity sposób przechodzenia przez elementy.
Wzorzec oddziela logikę przechodzenia od samej kolekcji. Obejmuje dwa główne komponenty: interfejs Iterator, który definiuje metody przechodzenia przez elementy, oraz Iterable (lub Aggregate), który tworzy iteratory dla swojej kolekcji:
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++];
}
}
}Iterator przechowuje własny stan przechodzenia, umożliwiając wielu iteratorom niezależne przechodzenie po tej samej kolekcji. Klienci korzystają z iteratora, nie wiedząc, w jaki sposób kolekcja przechowuje dane:
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());
}Wbudowane interfejsy Java Iterable i Iterator są zgodne z tym dokładnym wzorcem, dlatego możesz używać rozszerzonych pętli for z każdą klasą implementującą Iterable. Wzorzec iteratora jest niezbędny, gdy potrzebujesz udostępnić wiele metod przechodzenia po elementach lub ukryć złożone struktury wewnętrzne przed kodem klienta.
Wyzwanie
ŁatwyZbudujmy system playlisty z użyciem wzorca Iteratora! Utworzysz playlistę muzyczną, która przechowuje utwory i udostępnia własny iterator pozwalający przechodzić przez nie kolejno, bez ujawniania sposobu ich wewnętrznego przechowywania. To idealne zastosowanie wzorca Iteratora, który pozwala użytkownikom przeglądać Twoją kolekcję za pomocą przejrzystego, jednolitego interfejsu.
Podzielisz swój kod na cztery pliki:
Iterator.java: Zdefiniuj generyczny interfejsIterator<T>z dwiema metodami:hasNext(), która zwraca wartość logiczną informującą, czy są jeszcze jakieś elementy, oraznext(), która zwraca następny element typu T.Playlist.java: Utwórz klasę agregującą, która przechowuje utwory. Twoja klasaPlaylistpowinna przechowywać utwory w tablicy String o stałej pojemności (przekazywanej do konstruktora) i śledzić, ile utworów dodano. Dodaj metodęaddSong(String song), aby dodawać utwory do playlisty.Twoja klasa Playlist potrzebuje metody
createIterator(), która zwracaIterator<String>. Zaimplementuj ją, tworząc prywatną klasę wewnętrzną o nazwiePlaylistIterator, która implementuje Twój interfejs Iterator. Ten iterator wewnętrzny przechowuje własną pozycję indeksu i przechodzi przez tablicę utworów, zwracając każdy utwór po kolei.Song.java: Utwórz prostą klasęSong, która przechowuje tytuł utworu. Powinna mieć konstruktor przyjmujący tytuł (String), metodęgetTitle()oraz metodętoString(), która zwracaPlaying: [title].Main.java: Połącz swój system iteratora! Otrzymasz jedno wejście: listę tytułów utworów rozdzielonych przecinkami (na przykład:Bohemian Rhapsody,Stairway to Heaven,Hotel California).Utwórz Playlist o pojemności 10 utworów. Przetwórz dane wejściowe i dodaj każdy tytuł utworu do playlisty. Następnie pobierz iterator z playlisty i użyj go, aby przejść przez wszystkie utwory, wypisując każdy opakowany w obiekt Song (który wyświetli się jako
Playing: [title]).Po przejściu przez wszystkie utwory wypisz
Playlist complete!w nowej linii.
Otrzymasz jedno wejście: ciąg znaków zawierający tytuły utworów rozdzielone przecinkami.
Na przykład dla wejścia Yesterday,Imagine,Let It Be wynikiem będzie:
Playing: Yesterday
Playing: Imagine
Playing: Let It Be
Playlist complete!Zwróć uwagę, że Twoja klasa Main używa metod iteratora hasNext() i next(), aby przejść przez playlistę, nie wiedząc nic o strukturze bazowej tablicy. Iterator hermetyzuje całą logikę przechodzenia, ukrywając wewnętrzną reprezentację kolekcji przed kodem klienta!
Spróbuj swoich sił
import java.util.Scanner;
class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
String input = scanner.nextLine();
// TODO: Utwórz Playlist o pojemności 10 utworów
// TODO: Przetwórz dane wejściowe (rozdzielone przecinkami) i dodaj każdy tytuł utworu do listy odtwarzania
// TODO: Pobierz iterator z listy odtwarzania za pomocą createIterator()
// TODO: Użyj iteratora, aby przejść przez wszystkie utwory
// Dla każdego utworu utwórz obiekt Song i go wypisz
// TODO: Wypisz "Playlist complete!" po przejściu przez wszystkie utwory
}
}
Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.
Wszystkie lekcje w sekcji Programowanie obiektowe
1Podstawy programowania obiektowego
Pliki zewnętrzneWprowadzenie do programowania obiektowegoKlasy a obiektySłowo kluczowe thisMetodyPola (atrybuty)Metoda konstruktoraPrzeciążanie konstruktoraPodsumowanie — prosty kalkulator4Dziedziczenie
Podstawy dziedziczenia (extends)Słowo kluczowe superPrzesłanianie metod (@Override)Łańcuch wywołań konstruktorówKlasa ObjectDziedziczenie jedno- i wielopoziomoweDlaczego Java nie obsługuje wielodziedziczenia klasPodsumowanie – hierarchia pracowników7Metody specjalne i klasa Object
Metoda toString()equals() i hashCode()Metoda clone()compareTo() i ComparableInterfejs ComparatorPodsumowanie — własne sortowanie10Obsługa wyjątków w programowaniu obiektowym
Hierarchia klas wyjątkówWłasne wyjątkiBłędy sprawdzane a niesprawdzaneWzorzec try-with-resourcesPodsumowanie – zweryfikowany użytkownik2Modyfikatory dostępu i enkapsulacja
Przegląd poziomów dostępuMetody pobierające i ustawiająceUkrywanie informacjiSłowo kluczowe finalPodsumowanie — menedżer kont bankowych5Polimorfizm
Podstawy przeciążania metodPrzesłanianie metod (w czasie wykonywania)Rzutowanie w górę i w dółOperator instanceofKlasy i metody abstrakcyjnePowtórka — kalkulator figur8Zaawansowane koncepcje OOP
Kompozycja a dziedziczenieAgregacja a kompozycjaKlasy wewnętrzne, zagnieżdżone i anonimoweEnumy i metody enumówRekordy (Java 16+)Klasy zapieczętowane (Java 17+)11Wzorce projektowe — część 1
Wprowadzenie do wzorców projektowychWzorzec SingletonWzorzec FactoryWzorzec BuilderWzorzec ObserverWzorzec Strategy3Właściwości klas i składowe statyczne
Zmienne instancji a zmienne statyczneMetody statyczneBloki statyczneStałe (static final)Powtórka – Counter i Utility6Interfejsy i klasy abstrakcyjne
Wprowadzenie do interfejsówImplementowanie interfejsówImplementacja wielu interfejsówMetody domyślne i statyczne w interfejsieKlasy abstrakcyjne a interfejsyInterfejsy funkcyjnePodsumowanie — system płatności9Typy generyczne
Wprowadzenie do typów generycznychKlasy generyczneMetody generyczneOgraniczone parametry typówSymbole wieloznaczne (?, extends, super)Powtórzenie – kontener generyczny12Wzorce projektowe, część 2
Wzorzec poleceniaWzorzec adapteraWzorzec dekoratoraWzorzec metody szablonowejWzorzec stanuWzorzec kompozytuWzorzec iteratoraPoćwicz samodzielnie: Kompilator Java online