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

نمط المكرر

جزء من قسم Object Oriented Programming في رحلة Java على Coddy — الدرس 76 من 87.

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

يفصل هذا النمط منطق التنقل عن المجموعة نفسها. ويتكون من مكونين رئيسيين: واجهة 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) على حالة التنقل الخاصة به، مما يسمح لعدة مكررات بالتنقل عبر نفس المجموعة بشكل مستقل. يستخدم العملاء المكرر دون معرفة كيفية تخزين المجموعة لبياناتها:

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

تتبع واجهات Iterable و Iterator المدمجة في Java هذا النمط تماماً، ولهذا السبب يمكنك استخدام حلقات for المحسنة مع أي فئة تقوم بتنفيذ Iterable. يعد نمط المكرر (Iterator Pattern) ضرورياً عندما تحتاج إلى توفير طرق تنقل متعددة أو إخفاء الهياكل الداخلية المعقدة عن كود العميل.

challenge icon

التحدي

سهل

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

ستقوم بتنظيم الكود الخاص بك عبر أربعة ملفات:

  • Iterator.java: قم بتعريف واجهة Iterator<T> العامة مع طريقتين: hasNext() التي تعيد قيمة منطقية (boolean) تشير إلى ما إذا كانت هناك عناصر أخرى موجودة، و next() التي تعيد العنصر التالي من النوع T.
  • Playlist.java: أنشئ الفئة المجمعة (aggregate class) التي تحمل أغانيك. يجب أن تخزن Playlist الأغاني في مصفوفة String بسعة ثابتة (يتم تمريرها إلى المنشئ) وتتبع عدد الأغاني التي تمت إضافتها. قم بتضمين طريقة addSong(String song) لإضافة الأغاني إلى قائمة التشغيل.

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

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

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

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

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

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

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

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

ورقة مرجعية

يُعد نمط المكرر (Iterator Pattern) نمط تصميم سلوكي يوفر طريقة للوصول إلى عناصر مجموعة ما بشكل تسلسلي دون الكشف عن بنيتها الأساسية.

يفصل هذا النمط منطق التنقل عن المجموعة نفسها باستخدام مكونين رئيسيين:

  • واجهة Iterator: تحدد طرق التنقل عبر العناصر
  • Iterable/Container: ينشئ مكررات (iterators) لمجموعته

البنية الأساسية

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

يحتفظ المكرر بحالة التنقل الخاصة به، مما يسمح لعدة مكررات بالتنقل في نفس المجموعة بشكل مستقل. تتبع واجهات Iterable و Iterator المدمجة في Java هذا النمط، مما يتيح استخدام حلقات for المحسنة (enhanced for-loops).

جرّب بنفسك

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: اطبع "Playlist complete!" بعد المرور على جميع الأغاني
    }
}
quiz iconاختبر نفسك

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

جميع دروس Object Oriented Programming