Menu
Coddy logo textTech

תבנית האיטרטור

חלק מהיחידה תכנות מונחה עצמים במסלול ה-Java של Coddy. שיעור 76 מתוך 87.

תבנית האיטרטור היא תבנית עיצוב התנהגותית שמספקת דרך לגשת ברצף לאיברים באוסף, בלי לחשוף את המבנה הפנימי שלו. בין אם עובדים עם מערך, רשימה מקושרת או עץ, האיטרטור מאפשר לעבור על האיברים באופן אחיד.

התבנית מפרידה את לוגיקת המעבר מהאוסף עצמו. היא כוללת שני רכיבים עיקריים: ממשק Iterator שמגדיר שיטות למעבר בין איברים, ו־Iterable (או Aggregate) שיוצר איטרטורים עבור האוסף שלו:

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++];
        }
    }
}

האיטרטור שומר את מצב המעבר שלו, וכך מאפשר למספר איטרטורים לעבור על אותו אוסף באופן עצמאי. לקוחות משתמשים באיטרטור בלי לדעת כיצד האוסף מאחסן את הנתונים שלו:

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());
}

הממשקים המובנים של Java, Iterable ו־Iterator, פועלים לפי הדפוס המדויק הזה, ולכן אפשר להשתמש בלולאות for מורחבות עם כל מחלקה שמממשת את Iterable. דפוס האיטרטור חיוני כשצריך לספק כמה שיטות למעבר על אוסף או להסתיר מבנה פנימי מורכב מקוד הלקוח.

challenge icon

אתגר

קל

בואו נבנה מערכת של רשימת השמעה באמצעות תבנית Iterator! תיצור רשימת השמעה של מוזיקה שמאחסנת שירים ומספקת איטרטור מותאם אישית כדי לעבור עליהם ברצף, בלי לחשוף את אופן האחסון הפנימי של השירים. זהו מקרה שימוש מושלם בתבנית Iterator, שמאפשרת למשתמשים לנווט באוסף שלך באמצעות ממשק אחיד ופשוט.

תארגן את הקוד שלך בארבעה קבצים:

  • Iterator.java: הגדר את הממשק הגנרי שלך Iterator<T> עם שתי מתודות: hasNext() שמחזירה ערך בוליאני המציין אם קיימים איברים נוספים, ו-next() שמחזירה את האיבר הבא מסוג T.
  • Playlist.java: צור את מחלקת האוסף שמחזיקה את השירים שלך. המחלקה Playlist שלך צריכה לאחסן שירים במערך String בעל קיבולת קבועה (שנמסרת לבנאי) ולעקוב אחר מספר השירים שנוספו. הוסף מתודה addSong(String song) להוספת שירים לרשימת ההשמעה.

    מחלקת Playlist שלך צריכה לכלול מתודה createIterator() שמחזירה Iterator<String>. ממש את המתודה באמצעות יצירת מחלקה פנימית פרטית בשם PlaylistIterator שמממשת את ממשק Iterator שלך. האיטרטור הפנימי הזה מנהל מיקום אינדקס משלו ועובר על מערך השירים, תוך החזרת כל שיר לפי הסדר.

  • Song.java: צור מחלקת Song פשוטה שעוטפת כותרת של שיר. עליה לכלול בנאי שמקבל את הכותרת (String), מתודה getTitle() ומתודה toString() שמחזירה Playing: [title].
  • Main.java: חבר את מערכת האיטרטור שלך! תקבל קלט אחד: רשימה של כותרות שירים המופרדות בפסיקים (לדוגמה: Bohemian Rhapsody,Stairway to Heaven,Hotel California).

    צור רשימת השמעה עם קיבולת של 10 שירים. נתח את הקלט והוסף כל כותרת של שיר לרשימת ההשמעה. לאחר מכן קבל איטרטור מרשימת ההשמעה והשתמש בו כדי לעבור על כל השירים, ולהדפיס כל אחד מהם כשהוא עטוף באובייקט Song (שיוצג כך: Playing: [title]).

    לאחר המעבר על כל השירים, הדפס Playlist complete! בשורה חדשה.

תקבל קלט אחד: מחרוזת עם כותרות שירים המופרדות בפסיקים.

לדוגמה, עבור הקלט Yesterday,Imagine,Let It Be, הפלט שלך יהיה:

Playing: Yesterday
Playing: Imagine
Playing: Let It Be
Playlist complete!

שים לב איך מחלקת Main שלך משתמשת במתודות hasNext() ו-next() של האיטרטור כדי לעבור על רשימת ההשמעה, מבלי לדעת דבר על המבנה של המערך הבסיסי. האיטרטור עוטף את כל לוגיקת המעבר, וכך משאיר את הייצוג הפנימי של האוסף מוסתר מקוד הלקוח!

נסו בעצמכם

import java.util.Scanner;

class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        String input = scanner.nextLine();
        
        // TODO: צור Playlist עם קיבולת של 10 שירים
        
        // TODO: פענח את הקלט (מופרד בפסיקים) והוסף כל שם של שיר לרשימת ההשמעה
        
        // TODO: קבל איטרטור מרשימת ההשמעה באמצעות createIterator()
        
        // TODO: השתמש באיטרטור כדי לעבור על כל השירים
        // עבור כל שיר, עטוף אותו באובייקט Song והדפס אותו
        
        // TODO: הדפס "Playlist complete!" לאחר מעבר על כל השירים
    }
}
quiz iconבחנו את עצמכם

השיעור הזה כולל חידון קצר. התחילו את השיעור כדי לענות עליו ולעקוב אחרי ההתקדמות.

כל השיעורים ביחידה תכנות מונחה עצמים

תרגלו בעצמכם: קומפיילר Java אונליין