تعدد الأشكال في لغة JavaScript


يا هلا والله بالجميع! اليوم بنتكلم عن مفهوم مهم جداً في البرمجة الكائنية التوجه (OOP) وهو تعدد الأشكال (Polymorphism) في جافاسكريبت. لا تشيل هم، الموضوع أبسط مما تتخيل.

وش يعني تعدد الأشكال (Polymorphism)؟

باختصار شديد، تعدد الأشكال يعني قدرة الكائن على اتخاذ أشكال متعددة، أو قدرة دالة معينة على التصرف بطرق مختلفة حسب الكائن اللي تتعامل معاه. يعني عندك "واجهة" وحدة، بس التنفيذ يختلف.

تخيل عندك زر واحد "تشغيل"، بس إذا ضغطته على التلفزيون يشغل التلفزيون، وإذا ضغطته على المكيف يشغل المكيف. هذا هو تعدد الأشكال ببساطة!

تعدد الأشكال عن طريق تجاوز الدوال (Method Overriding)

هذا من أشهر أنواع تعدد الأشكال في جافاسكريبت، ويصير لما يكون عندك كلاس أب (Parent Class) وكلاسات أبناء (Child Classes) ترث منه. الكلاس الابن يقدر يعطي تنفيذ خاص فيه لدالة موجودة أصلاً في الكلاس الأب.

مثال عملي:

عندنا كلاس Animal وفيه دالة makeSound(). بعدين عندنا كلاسين Dog و Cat يرثون من Animal، وكل واحد منهم بيسوي تنفيذ خاص فيه لدالة makeSound().

class Animal {
  makeSound() {
    console.log("صوت حيوان!");
  }
}

class Dog extends Animal {
  makeSound() {
    console.log("الكلب ينبح: هوف هوف!");
  }
}

class Cat extends Animal {
  makeSound() {
    console.log("القطة تموء: مواء مواء!");
  }
}

const myDog = new Dog();
const myCat = new Cat();
const myAnimal = new Animal();

myDog.makeSound();    // الكلب ينبح: هوف هوف!
myCat.makeSound();    // القطة تموء: مواء مواء!
myAnimal.makeSound(); // صوت حيوان!

// هذا هو تعدد الأشكال! الدالة نفسها (makeSound) بس تتصرف بشكل مختلف
// حسب نوع الكائن اللي استدعاها.

تعدد الأشكال بالـ Duck Typing (التعامل كالبطة)

جافاسكريبت ما فيها "Interfaces" صريحة زي لغات ثانية، لكن نقدر نحقق تعدد الأشكال بطريقة نسميها "Duck Typing". معناها باختصار: "إذا مشى زي البطة، وصاح زي البطة، فهو بطة!". يعني إذا الكائن عنده الدالة اللي نحتاجها، نقدر نتعامل معاه على إنه من النوع اللي نبغاه، بغض النظر عن الكلاس اللي ينتمي له.

مثال عملي:

عندنا دالة اسمها describe(entity). هذي الدالة تتوقع إن الكائن اللي يجيها عنده دالة اسمها getInfo(). ما يهمنا وش نوع الكائن، المهم عنده هذي الدالة.

class Car {
  constructor(model) {
    this.model = model;
  }
  getInfo() {
    return <code dir="ltr" style="background:#f3f4f6; color:#0056b3; padding:2px 6px; border-radius:4px; font-family:monospace; direction:ltr !important; display:inline-block;">هذي سيارة موديلها: ${this.model}</code>;
  }
}

class Robot {
  constructor(name) {
    this.name = name;
  }
  getInfo() {
    return <code dir="ltr" style="background:#f3f4f6; color:#0056b3; padding:2px 6px; border-radius:4px; font-family:monospace; direction:ltr !important; display:inline-block;">هذا روبوت اسمه: ${this.name}</code>;
  }
}

function printEntityInfo(entity) {
  // طالما الكائن عنده دالة getInfo()، نقدر نتعامل معاه
  if (typeof entity.getInfo === 'function') {
    console.log(entity.getInfo());
  } else {
    console.log("هذا الكائن ما عنده دالة getInfo().");
  }
}

const myCar = new Car("كامري");
const myRobot = new Robot("وال-إي");
const someNumber = 123;

printEntityInfo(myCar);     // هذي سيارة موديلها: كامري
printEntityInfo(myRobot);   // هذا روبوت اسمه: وال-إي
printEntityInfo(someNumber); // هذا الكائن ما عنده دالة getInfo().

لاحظ هنا إن دالة printEntityInfo ما تعرف إذا اللي بيجيها Car ولا Robot، بس تعرف إنها تحتاج دالة getInfo(). وهذا يعطينا مرونة رهيبة في الكود!

ليش تعدد الأشكال مهم؟

  • مرونة الكود: يخلي الكود حقك مرن وقابل للتوسع. تقدر تضيف أنواع جديدة من الكائنات بدون ما تعدل الدوال اللي تتعامل معاها.
  • سهولة الصيانة: يقلل من تكرار الكود ويخلي الكود أنظف وأسهل في القراءة والصيانة.
  • إعادة الاستخدام: يسمح لك بإعادة استخدام نفس الواجهة (اسم الدالة مثلاً) مع تطبيقات مختلفة.

وبكذا نكون غطينا أساسيات تعدد الأشكال في جافاسكريبت. طبقوا الأمثلة هذي عشان ترسخ المعلومة. بالتوفيق!