
الوراثة المتعددة multi inheritance في Dart
تعني عندما ترث فئة من فئات متعددة أو بمعنى أصح أن يرث الكلاس من أكثر من كلاس.
الوراثة متعددة المستويات في dart هي عندما ترث الكلاسات المختلفة في شكل سلسلة أي أن أحد الكلاسات يمتد إلى كلاسات الأصل بينما يمتد الكلاس الآخر إلى الكلاسات التي كانت توسع الكلاسات الأصلية.
في لغة برمجة Dart، لا يدعم مفهوم الوراثة المتعددة (multi inheritance) مباشرة. وراثة المتعددة تعني أن الصنف يمكنه أن يرث سمات من أكثر من صنف واحد في نفس الوقت. هذا يعني أنه يمكن للصنف أن يكون له أكثر من صنف أساسي.
في لغة برمجة Dart، لا يدعم المفهوم المباشر للوراثة المتعددة (multi inheritance)، وذلك لأسباب تتعلق بتصميم اللغة. في Dart، يتم استخدام نظام التركيب (composition) بدلاً من الوراثة المتعددة لتحقيق هذا الغرض.
بدلاً من تمديد (extend) عدة صناديق (classes) في صندوق واحد، يمكنك إنشاء صندوق جديد يحتوي على مكونات (components) من الصناديق المختلفة. يمكن أن تكون هذه المكونات عبارة عن صناديق أخرى أو واجهات (interfaces) أو مجرد وظائف (functions)، وتوفر لك هذه الطريقة مرونة كبيرة في تكوين سلوك الصندوق الجديد.
لنلقي نظرة على مثال بسيط يوضح التركيب في Dart:
class A { void methodA() { print('Method A'); } } class B { void methodB() { print('Method B'); } } class C { A a = A(); B b = B(); void methodC() { a.methodA(); b.methodB(); } } void main() { C c = C(); c.methodC(); // ستقوم بطباعة "Method A" و "Method B" }
في هذا المثال، تم إنشاء صندوق C الذي يحتوي على مكونين A و B. يمكن استخدام المكونات لاستدعاء الوظائف methodA و methodB بشكل منفصل، وبالتالي يتم تحقيق فعالية وظيفتين من صناديق مختلفة.
على الرغم من أن الوراثة المتعددة ليست مدعومة مباشرة في Dart، إلا أنه يمكن استخدام واجهات والتركيب لتحقيق مفهوم المرونة وإعادة استخدام الكود بشكل فعال في التطبيقات.
ومع ذلك، يوفر لغة Dart طرقًا بديلة لتحقيق بعض مفهوم الوراثة المتعددة. تتضمن هذه الأساليب:
- الوراثة مع الواجهات (Interfaces Inheritance): يمكن لصنف في Dart أن ينفذ واجهة (interface) واحدة أو أكثر. يُفترض أن الواجهات تحدد السمات التي يجب أن يتصرف بها الصنف الذي ينفذها. يمكن للصنف أن ينفذ سمات متعددة من خلال تنفيذ واجهات متعددة.
- الوراثة المتعددة باستخدام الصنف الأبستراكتي (Abstract Class Inheritance): يمكن لصنف في Dart أن يرث من صنف واحد فقط، ولكن يمكن للصنف الأب أن يكون صنفًا أبستراكتيًا (abstract class) يحتوي على تعريفات للسمات التي يمكن للصنف الفرعي تنفيذها. وبالتالي، يمكن للصنف الفرعي أن يرث من الصنف الأبستراكتي ويحصل على التنفيذ المشترك للسمات.
الشكل العام للوراثة متعددة المستويات
class A {} class B extends A {} class C extends B {}
لاحظنا بناء الجملة يمكننا أن نرى بوضوح أن الكلاس A هو الكلاس الأصلي للكلاس B و التي تعمل على توسيعها أيضاً تعمل الكلاس B كوالد للكلاس C و التي تمتد إلى الكلاس B.
مثال على الوراثة المتعددة باستخدام الواجهات:
class A { void printA() { print("A"); } } class B { void printB() { print("B"); } } class C implements A, B { void printC() { print("C"); } void printA() { print("Overridden A"); } void printB() { print("Overridden B"); } } void main() { C c = C(); c.printA(); // ستطبع "Overridden A" c.printB(); // ستطبع "Overridden B" c.printC(); // ستطبع "C" }
في هذا المثال، يتم تنفيذ الواجهتين A و B في الصنف C. يتم تنفيذ السمات المحددة في الواجهتين ويمكن أيضًا تجاوزها في الصنف C.
على الرغم من أن الوراثة المتعددة المباشرة غير مدعومة في Dart، يمكن استخدام الواجهات والصنف الأبستراكتي لتحقيق بعض النماذج الشبيهة بالوراثة المتعددة.
مثال
class Wood{ void printName(){ print("Inside class Wood"); } } class Table extends Wood{ void printTable(){ print("Inside Table class"); } } class TableLegs extends Table{ void printTableLegs(){ print("Inside TableLegs class"); } } void main(){ TableLegs tl = new TableLegs(); tl.printTableLegs(); tl.printTable(); tl.printName(); }
في المثال أعلاه لدينا ثلاث كلاسات مختلفة وهي Wood و Table و TableLegs و داخل الدالة الرئيسية نقوم بإنشاء كائن من الكلاس TableLegs ثم إستدعاء ميثود الكلاس الأصلية التي يمتد إليها الكلاس.
عند تشغيل الكود ستكون النتيجة
Inside TableLegs class Inside Table class Inside class Wood
بعض الأنماط الشبيهة بالوراثة المتعددة يمكن تحقيقها باستخدام تركيبة من الواجهات والصنف الأبستراكتي في Dart. هناك نمطين شائعين:
تركيبة الواجهات (Interface Composition): يمكن تحقيق الوراثة المتعددة عن طريق تنفيذ واجهات متعددة وتجميع سماتها في الصنف الفرعي. يمكن للصنف الفرعي تنفيذ عدة واجهات والاستفادة من سماتها وتنفيذها.
class A { void printA() { print("A"); } } class B { void printB() { print("B"); } } class C implements A, B { void printC() { print("C"); } } void main() { C c = C(); c.printA(); // ستطبع "A" c.printB(); // ستطبع "B" c.printC(); // ستطبع "C" } ``` في هذا المثال، يتم تنفيذ الواجهتين A و B في الصنف C، وبالتالي يمكن للصنف C استخدام سمات كلا الواجهتين.
الوراثة المتعددة باستخدام صنف أبستراكتي وواجهات (Abstract Class and Interfaces): يمكن استخدام صنف أبستراكتي لتحقيق سمات مشتركة وإنشاء واجهات للتفاعل مع تلك السمات. يمكن للصنف الفرعي تنفيذ الواجهات وتوفير تنفيذ للسمات المحددة في الصنف الأبستراكتي.
مثال على الوراثة المتعددة باستخدام الواجهات:
class A { void printA() { print("A"); } } class B { void printB() { print("B"); } } class C implements A, B { void printC() { print("C"); } void printA() { print("Overridden A"); } void printB() { print("Overridden B"); } } void main() { C c = C(); c.printA(); // ستطبع "Overridden A" c.printB(); // ستطبع "Overridden B" c.printC(); // ستطبع "C" }
في هذا المثال، يتم تنفيذ الواجهتين A و B في الصنف C. يتم تنفيذ السمات المحددة في الواجهتين ويمكن أيضًا تجاوزها في الصنف C.
على الرغم من أن الوراثة المتعددة المباشرة غير مدعومة في Dart، يمكن استخدام الواجهات والصنف الأبستراكتي لتحقيق بعض النماذج الشبيهة بالوراثة المتعددة.
مثال استخدام واجهات:
يمكنك تعريف واجهات متعددة ومن ثم تنفيذها في صندوق واحد. هذا يعطي الصندوق سلوكًا يشبه الوراثة المتعددة.
class A { void methodA() { print('Method A'); } } class B { void methodB() { print('Method B'); } } class C implements A, B { void methodA() { print('Method A from C'); } void methodB() { print('Method B from C'); } } void main() { C c = C(); c.methodA(); // ستقوم بطباعة "Method A from C" c.methodB(); // ستقوم بطباعة "Method B from C" }
في هذا المثال، يتم تعريف صناديق A و B، ثم يتم تعريف صندوق C الذي ينفذ كلتاهما. بالتالي، يكتسب صندوق C سلوكًا مشتركًا من الصناديق A و B.
مثال استخدام التركيب:
class A { void methodA() { print('Method A'); } } class B { void methodB() { print('Method B'); } } class C { A a = A(); B b = B(); void methodC() { a.methodA(); b.methodB(); } } void main() { C c = C(); c.methodC(); // ستقوم بطباعة "Method A" و "Method B" }
في هذا المثال، يتم إنشاء صناديق A
و B
، ثم يتم إنشاء صندوق C
الذي يحتوي على مكونات A
و B
. يمكن استخدام المكونات لاستدعاء وظائف الصناديق المختلفة وتحقيق سلوك متعدد في الصندوق الجديد.
ملحوظة: عند استخدام الواجهات أو التركيب لتحقيق سلوك يشبه الوراثة المتعددة في Dart، قد تحتاج إلى إدارة الصراعات المحتملة بين الوظائف المكررة أو التضاربات في الأسماء يدويًا.