تعدد الأشكال والإرسال الديناميكي
تعدد الأشكال والإرسال الديناميكي
تعلّمت حتى الآن كيفية إنشاء الصفوف الفرعية وتجاوز التوابع. الآن تأتي الفكرة التي تجمع كل ذلك معًا: تعدد الأشكال — قدرة متغيّر واحد على التصرف كأنه كائنات من أنواع مختلفة. مقترنًا بـالإرسال الديناميكي، وهو الطريقة التي تقرر بها Java وقت التشغيل أيّ نسخة من التابع المُتجاوَز ستستدعي، يصبح تعدد الأشكال العمود الفقري للتصميم المرن والقابل للتوسع.
مرجع من النوع الأب لكائن من النوع الابن
في Java، يمكن لمتغيّر من نوع الصف الأب أن يحمل مرجعًا لأي كائن ابن. هذا مشروع تمامًا لأن الابن هو نوع من الأب:
المتغيّر a مُعلَن من نوع Animal، لكنه يشير إلى كائن Dog. عند استدعاء speak()، لا تستخدم Java نسخة Animal — بل تستخدم نسخة الكائن الفعلي. هذا القرار يحدث وقت التشغيل، لا وقت الترجمة. هذا هو الإرسال الديناميكي.
لماذا يهمّنا هذا؟ البرمجة وفق النوع
تظهر القوة الحقيقية حين تكتب كودًا يعمل مع النوع الأب، وبالتالي يعمل مع أي صف ابن حالي أو مستقبلي. يُسمّى هذا المبدأ البرمجة وفق النوع (ويُعبَّر عنه أحيانًا بـ "برمجِ وفق الواجهة لا وفق التنفيذ").
كُتبت makeAnimalSpeak مرة واحدة ولن تحتاج أبدًا للتغيير، حتى لو أضفت صف Lion غدًا. يكفي أن يتجاوز الصف الجديد speak()، والإرسال الديناميكي يتولى الباقي.
تعدد الأشكال مع المصفوفات والقوائم
تخزين كائنات ابن مختلطة تحت نوع أب مشترك في مجموعة هو نمط كلاسيكي:
الحلقة لا تحتاج لمعرفة النوع الملموس أو الاكتراث به. الناتج هو:
Animal a = new Dog() على Dog a = new Dog() حين لا يحتاج بقية الكود سوى سلوك Animal. هذا يتيح لك استبدال النوع الملموس لاحقًا دون تغيير كل سطر يستخدم a.
ما لا يشمله الإرسال الديناميكي
ينطبق الإرسال الديناميكي على توابع النسخة فقط. لا ينطبق على:
- الحقول — إن أعلن الأب والابن كلاهما حقلًا بالاسم ذاته، فنوع المرجع هو الذي يحدد أي حقل يُقرأ، لا نوع الكائن.
- التوابع الثابتة (static) — تنتمي التوابع الثابتة للصف لا للكائنات، لذا تُحسم وقت الترجمة بناءً على نوع المرجع.
مثال ملموس: مساحة الأشكال الهندسية
إليك مثالًا متكاملًا يمكنك تجميعه وتشغيله، يوضح كيف يختار الإرسال الديناميكي التابع area() الصحيح وقت التشغيل:
كُتبت printArea وفق نوع Shape. ستعمل بشكل صحيح مع كل صف فرعي من Shape قد يُنشأ في المستقبل، دون أي تعديل.
الخلاصة
- يمكن لمتغيّر من نوع الأب أن يحمل مرجعًا لأي كائن ابن.
- الإرسال الديناميكي يضمن استدعاء التابع المتجاوَز للكائن الفعلي وقت التشغيل.
- البرمجة وفق النوع تعني كتابة الكود وفق النوع الأب ليعمل مع جميع الأبناء — الحاليين والمستقبليين.
- ينطبق الإرسال الديناميكي على توابع النسخة فقط، لا على الحقول ولا على التوابع الثابتة.