نمط المكرر
جزء من قسم البرمجة كائنية التوجه في رحلة 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());
}تتبع الواجهتان المضمنّتان Iterable وIterator في Java هذا النمط نفسه تمامًا، ولهذا يمكنك استخدام حلقات for المحسّنة مع أي فئة تنفّذ Iterable. يُعد نمط المكرّر أساسيًا عندما تحتاج إلى توفير طرق متعددة لاجتياز العناصر أو إخفاء البُنى الداخلية المعقدة عن شيفرة العميل.
التحدي
سهللنبنِ نظام قائمة تشغيل باستخدام نمط المكرّر! ستنشئ قائمة تشغيل موسيقية تخزّن الأغاني وتوفّر مكرّرًا مخصّصًا للتنقّل بينها بالتسلسل، دون الكشف عن كيفية تخزين الأغاني داخليًا. يُعدّ هذا مثالًا مثاليًا لاستخدام نمط المكرّر، إذ يتيح للمستخدمين التنقّل في مجموعتك عبر واجهة واضحة وموحّدة.
ستنظّم التعليمات البرمجية عبر أربعة ملفات:
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).أنشئ 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() الخاصتين بالمكرّر للتنقّل في قائمة التشغيل دون معرفة أي شيء عن بنية المصفوفة الأساسية. يغلّف المكرّر منطق التنقّل بالكامل، ويُبقي التمثيل الداخلي للمجموعة مخفيًا عن تعليمات العميل البرمجية!
جرّب بنفسك
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!" بعد المرور على جميع الأغاني
}
}
يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.
جميع دروس البرمجة كائنية التوجه
1أساسيات الـ OOP
الملفات الخارجيةمقدمة في الـ OOPالفئات (Classes) مقابل الكائنات (Objects)الكلمة المفتاحية thisالدوال (Methods)الحقول (Attributes)دالة البناء (Constructor)تعدد أشكال دالة البناء (Constructor Overloading)مراجعة - آلة حاسبة بسيطة4الوراثة
الوراثة الأساسية (extends)الكلمة المفتاحية superإعادة تعريف الدوال (@Override)تسلسل المنشئات (Constructor Chaining)كلاس Objectالوراثة الأحادية ومتعددة المستوياتلماذا لا توجد وراثة متعددة للكلاساتمراجعة - هيكلية الموظفين7الدوال الخاصة وفئة Object
دالة toString()equals() و hashCode()دالة clone()compareTo() و Comparableواجهة Comparatorمراجعة - الترتيب المخصص2محددات الوصول والتغليف
نظرة عامة على مستويات الوصولدوال Getter و Setterإخفاء البياناتالكلمة المفتاحية finalمراجعة - مدير الحساب البنكي5تعدد الأشكال (Polymorphism)
أساسيات الـ Method Overloadingالـ Method Overriding (في وقت التشغيل)الـ Upcasting والـ Downcastingمعامل الـ instanceofالفئات والدوال المجردة (Abstract)مراجعة - حاسبة الأشكال8مفاهيم OOP المتقدمة
الـ Composition مقابل الـ Inheritanceالـ Aggregation مقابل الـ Compositionالفئات الداخلية والمتداخلة والمجهولة (Inner, Nested & Anonymous)الـ Enums ودوالهاالـ Records (Java 16+)الـ Sealed Classes (Java 17+)11أنماط التصميم - الجزء الأول
مقدمة في أنماط التصميمنمط Singletonنمط Factoryنمط Builderنمط Observerنمط Strategy3خصائص الفئة والعضو الساكن
المتغيرات الخاصة بالكائن مقابل المتغيرات الساكنةالدوال الساكنةالكتل البرمجية الساكنةالثوابت (static final)ملخص - العداد والأدوات المساعدة6الواجهات (Interfaces) والأصناف المجردة (Abstract Classes)
مقدمة في الواجهات (Interfaces)تنفيذ الواجهات (Interfaces)تنفيذ واجهات متعددةالـ Default و Static في الواجهاتالأصناف المجردة مقابل الواجهاتالواجهات الوظيفية (Functional Interfaces)مراجعة - نظام الدفع9الأنواع العامة (Generics)
مقدمة في الأنواع العامةالأصناف العامة (Generic Classes)الدوال العامة (Generic Methods)معاملات الأنواع المقيدةالرموز البديلة (?, extends, super)مراجعة - الحاويات العامة12أنماط التصميم - الجزء الثاني
نمط الأمرنمط المحولنمط المزيننمط طريقة القالبنمط الحالةنمط التركيبنمط المكررتدرّب بنفسك: مترجم Java عبر الإنترنت