Menu
Coddy logo textTech
flag Ar iconالعربيةdown icon

نمط المكرر

جزء من قسم البرمجة كائنية التوجه في رحلة Java على Coddy. الدرس 76 من 87.

يُعد Iterator Pattern نمط تصميم سلوكيًا يوفّر طريقة للوصول إلى عناصر مجموعة بالتتابع دون كشف بنيتها الأساسية. سواء كنت تعمل مع array أو قائمة مرتبطة أو شجرة، يمنحك iterator طريقة موحّدة لاجتياز العناصر.

يفصل النمط منطق الاجتياز عن المجموعة نفسها. ويتضمن مكوّنين رئيسيين: واجهة 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++];
        }
    }
}

يحافظ iterator على حالة الاجتياز الخاصة بـ its، مما يتيح لعدة كائنات iterator اجتياز المجموعة نفسها بشكل مستقل. يستخدم العملاء iterator من دون معرفة كيفية تخزين المجموعة لبياناتها:

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. يُعد نمط Iterator ضروريًا عندما تحتاج إلى توفير أساليب متعددة للتنقّل أو إخفاء الهياكل الداخلية المعقّدة عن كود العميل.

challenge icon

التحدي

سهل

لنُنشئ نظام قائمة تشغيل باستخدام نمط Iterator! ستنشئ قائمة تشغيل موسيقية تخزّن الأغاني وتوفّر مكرّرًا مخصّصًا للتنقّل بينها بالتسلسل، من دون كشف كيفية تخزين الأغاني داخليًا. هذه حالة استخدام مثالية لنمط Iterator، إذ تتيح للمستخدمين التنقّل في مجموعتك من خلال واجهة نظيفة وموحّدة.

ستنظّم كودك عبر أربعة ملفات:

  • Iterator.java: Define واجهة Iterator<T> generic الخاصة بك مع طريقتين: hasNext() التي تُرجع قيمة boolean تشير إلى ما إذا كانت هناك عناصر أخرى، وnext() التي تُرجع العنصر التالي من النوع T.
  • Playlist.java: Create فئة Aggregate التي holds أغانيك. يجب أن تخزّن Playlist الأغاني في مصفوفة String ذات capacity ثابتة (يتم تمريرها إلى constructor) وتتتبّع عدد الأغاني التي تمت إضافتها. ضمّن طريقة addSong(String song) لإضافة الأغاني إلى قائمة التشغيل.

    تحتاج Playlist إلى طريقة createIterator() تُرجع Iterator<String>. Implement ذلك من خلال إنشاء فئة inner خاصة تُسمّى PlaylistIterator وتطبّق واجهة Iterator الخاصة بك. يحافظ هذا الـ iterator الداخلي على موضع index الخاص به، ويتنقّل عبر مصفوفة الأغاني، مُرجعًا كل أغنية بالترتيب.

  • Song.java: Create فئة Song بسيطة تغلّف عنوان أغنية. يجب أن تحتوي على constructor يستقبل العنوان (String)، وطريقة getTitle()، وطريقة toString() تُرجع Playing: [title].
  • Main.java: اجمع نظام iterator الخاص بك! ستتلقى input واحدًا: قائمة بعناوين الأغاني مفصولة بفواصل (على سبيل المثال: Bohemian Rhapsody,Stairway to Heaven,Hotel California).

    Create كائن Playlist بسعة 10 أغانٍ. حلّل input وأضف كل عنوان أغنية إلى قائمة التشغيل. ثم احصل على iterator من قائمة التشغيل واستخدمه للتنقّل عبر جميع الأغاني، مع طباعة كل أغنية مغلّفة داخل كائن Song (والذي سيظهر بالشكل Playing: [title]).

    بعد iterating عبر جميع الأغاني، اطبع Playlist complete! في سطر جديد.

ستتلقى input واحدًا: سلسلة مفصولة بفواصل من عناوين الأغاني.

على سبيل المثال، عند استخدام input Yesterday,Imagine,Let It Be، سيكون output الخاص بك:

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

لاحظ كيف تستخدم فئة Main الخاصة بك طريقتَي hasNext() وnext() الخاصتين بـ iterator للتنقّل عبر قائمة التشغيل من دون معرفة أي شيء عن بنية المصفوفة underlying. يغلّف iterator كل منطق التنقّل، ويحافظ على إخفاء التمثيل الداخلي للمجموعة عن كود العميل!

جرّب بنفسك

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: تحليل المدخلات (المفصولة بفواصل) وإضافة عنوان كل أغنية إلى الـ playlist
        
        // TODO: الحصول على iterator من الـ playlist باستخدام createIterator()
        
        // TODO: استخدام الـ iterator للتنقل عبر جميع الأغاني
        // لكل أغنية، قم بتغليفها في كائن Song واطبعها
        
        // TODO: Print "Playlist complete!" after iterating through all songs
    }
}
quiz iconاختبر نفسك

يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.

جميع دروس البرمجة كائنية التوجه

تدرّب بنفسك: مترجم Java عبر الإنترنت