
الوراثة inheritance في Dart
تعرَّف الوراثة في dart بأنها العملية التي يرث فيها كلاس واحد خصائص كلاس آخر و هي مفيدة لأنه نستطيع من خلالها إنشاء كلاس جديد من كلاس سابق موجود.
نستفيد من الوراثه هي أن نختصر الكود فبدلاً من إعادة بناء الدالة مرة أُخرى إذا كانت الدالة موجودة في الكلاس الأب. نستطيع إعادة إستخدامها مرة أُخرى بدلاً من بناءها و إستهلاك الذاكرة و إطالة الكود بحيث يكون البرنامج منظم و اجمل. إذاً نحن قلنا بأن الكلاس الذي يرث إسمه الكلاس الإبن و الكلاس الذي يورّث إسمه الكلاس الأب.
في لغة برمجة Dart، يُطلق مصطلح “الوراثة” (inheritance) على عملية تمكنك من إنشاء فئة جديدة تستفيد من خصائص وسلوكيات فئة موجودة بالفعل. يسمح لك ذلك بإنشاء تسلسل من الفئات المتدرجة (hierarchy)، حيث يمكنك إعادة استخدام الكود وتوسيع الوظائف بشكل هرمي.
الوراثة هي عنصر رئيسي في البرمجة الشيئية OOPS و بمساعدة الوراثة يمكن لكلاس واحد الإستفادة من جميع خصائص و خصائص كلاس آخر.
بشكل عام يوجد هناك كلاسين مطلوبان في الوراثة و هما:
- الكلاس الأب Parent : تُعرف الفئة التي ورثتها الفئة الأخرى باسم الفئة الأصلية. في بعض الأحيان ، نشير إليها أيضًا بالفئة الأساسية.
- الكلاس الإبن Child : تُعرف بالكلاسات التي ترث خصائص الكلاسات الأصلية الأب بالكلاسات الإبن أو الفرعية.
الشكل العام لوراثة كلاس يرث من كلاس آخر.
class Childclass extends ParentClass { ****** }
عندما تريد كلاسات الإبن أن ترث خصائص الكلاس الأب فإننا نستخدم الكلمة المحجوزة extends.
لتحقيق الوراثة في Dart، يمكنك استخدام الكلمة المفتاحية “extends” بعد تعريف الفئة الجديدة لتحدد الفئة الأساسية التي ترث منها. على سبيل المثال، إذا كان لديك فئة تسمى Person تحتوي على خصائص مثل name و age، يمكنك إنشاء فئة جديدة تسمى Student ترث من Person وتحتوي على خصائص إضافية مثل grade و studentId كما يلي:
class Person { String name; int age; Person(this.name, this.age); } class Student extends Person { String grade; int studentId; Student(String name, int age, this.grade, this.studentId) : super(name, age); }
في هذا المثال، Student هي الفئة المشتقة (subclass) التي ترث من الفئة الأساسية Person باستخدام الكلمة المفتاحية extends. يمكنك إضافة خصائص إضافية لـ Student وإعادة استخدام الخصائص الموجودة في Person باستخدام super في المُعرِّف (constructor) لـ Student.
باستخدام الوراثة، يمكنك الوصول إلى خصائص ووظائف Person من خلال كائنات Student. على سبيل المثال:
void main() { var student = Student('John', 20, 'A', 12345); print(student.name); // سيطبع: John print(student.age); // سيطبع: 20 }
هذا هو مثال بسيط يوضح كيفية استخدام الوراثة في Dart. يمكنك توسيع الفئات وتعديلها بحسب احتياجاتك لإنشاء هرم من الفئات المتدرجة وتنظيم الكود بشكل منطقي وقابل لإعادة الاستخدام.
مثال
class Human{ void walk(){ print("Humans walk!"); } } class Person extends Human{ void speak(){ print("That person can speak"); } } void main(){ Person p = new Person(); p.speak(); p.walk(); }
في المثال أعلاه لدينا كلاسين وهما Human and Person , الكلاس Human هو الأب و الكلاس Person هو الابن وهو يرث المشي walk () من الكلاس Human.
عند تشغيل الكود
That person can speak Humans walk!
إنشاء كلاس جديد يرث من أكثر من كلاس أساسي
في لغة برمجة Dart، لا يُسمح بالوراثة المتعددة (multiple inheritance)، وهذا يعني أنه غير ممكن إنشاء فئة جديدة ترث من أكثر من فئة أساسية في نفس الوقت.
تعتمد Dart بدلاً من ذلك على مفهوم الوراثة الواحدة (single inheritance)، حيث يمكن للفئة الجديدة أن ترث من فئة واحدة فقط. هذا الاختيار يأتي من أجل تجنب بعض التعقيدات المرتبطة بالوراثة المتعددة، مثل تضارب أسماء الطرق (method name conflicts) وعدم الوضوح في سياق الوراثة.
ومع ذلك، يمكنك استخدام واجهات (interfaces) في Dart لتحقيق بعض مفهوم الوراثة المتعددة عن طريق تنفيذ (implementing) واجهات متعددة في الفئة. يمكن للفئة أن تنفذ واجهات متعددة وبالتالي تكتسب سلوكًا وخصائص من هذه الواجهات.
ما هي الفوائد الرئيسية لاستخدام الوراثة الواحدة بدلاً من الوراثة المتعددة؟
استخدام الوراثة الواحدة بدلاً من الوراثة المتعددة في لغة برمجة مثل Dart يأتي مع عدد من الفوائد الرئيسية، وهي:
- تبسيط التصميم: الوراثة المتعددة يمكن أن تؤدي إلى تعقيدات وتضاربات في التصميم، حيث يصعب التعامل مع مشكلات مثل تضارب أسماء الطرق والاختلافات في التصور الهرمي للفئات. باستخدام الوراثة الواحدة، يكون التصميم أكثر بساطة ووضوحًا، مما يجعل الفهم والصيانة أسهل.
- تجنب التضاربات: الوراثة المتعددة يمكن أن تؤدي إلى تضاربات في السياق والتناقضات في سلوك الفئات المتعددة. باستخدام الوراثة الواحدة، يتم تجنب هذه التضاربات وتبقى العلاقة بين الفئات أكثر وضوحًا وتنظيمًا.
- تعزيز إعادة الاستخدام: الوراثة الواحدة تمنحك إمكانية إعادة استخدام الكود بشكل أكبر. يمكنك إنشاء فئات فرعية ترث السلوك والخصائص من الفئة الأساسية، وعند الحاجة إلى تغيير أو توسيع السلوك يمكنك القيام بذلك في الفئة الأساسية دون التأثير على الفئات الفرعية المشتقة.
- تحسين الصيانة: بفضل الوراثة الواحدة، يمكنك إجراء تغييرات في الفئة الأساسية وأن تنعكس هذه التغييرات تلقائيًا على الفئات المشتقة. هذا يجعل عملية الصيانة أسهل وأقل تعقيدًا، حيث لا يلزم تعديل العديد من الفئات في حالة وجود التغييرات.
- تحقيق مبدأ التقسيم الوظيفي: الوراثة الواحدة تساعد على تقسيم البرنامج إلى وحدات منفصلة ومستقلة، حيث يمكن أن تكون كل فئة مسؤولة عن وظيفة محددة. هذا يعمل على تحسين هيكلية البرنامج وإدارته بشكل أفضل.
على الرغم من أن الوراثة المتعددة قد تكون ذات قيمة في بعض الحالات، إلا أن الوراثة الواحدة توفر العديد من المزايا وتسهل فهم وصيانة الكود.
ما هي الحالات التي يكون فيها الوراثة المتعددة أفضل من الوراثة الواحدة؟
على الرغم من أن الوراثة الواحدة هي الأسلوب الأكثر استخدامًا وتفضيلًا في برمجة الكائنات، إلا أن هناك بعض الحالات التي يمكن أن تستفيد من الوراثة المتعددة. هنا بعض الحالات التي يمكن أن يكون فيها الوراثة المتعددة أفضل:
- وجود علاقة “هو-عبارة-عن” (is-a relationship): عندما يكون لديك حالة حيث يمكن تصنيف الكائن كـ “هو” نوع من نوعين مختلفين في نفس الوقت، فقد يكون مناسبًا استخدام الوراثة المتعددة. على سبيل المثال، إذا كان لديك تطبيق لنظام الحجز يتضمن عمليات الحجز للفنادق والرحلات الجوية، يمكن أن يكون من المنطقي أن تكون لديك فئات مشتقة من فئتين أساسيتين: فئة “Hotel” وفئة “Flight”، حيث يمكن لكل فئة مشتقة تحمل خصائص وسلوكيات محددة لهذه العناصر.
- الحاجة إلى استخدام وظائف مشابهة من فئات مختلفة: في بعض الحالات، قد تحتاج إلى استخدام وظائف مشابهة من عدة فئات مختلفة. في هذه الحالات، يمكن استخدام الوراثة المتعددة لتحقيق هذا الهدف. على سبيل المثال، إذا كان لديك نظام لإدارة مستندات يتضمن مستندات نصية ومستندات PDF ومستندات Word، يمكنك تعريف فئات مشتقة من فئة “Document” لكل نوع من أنواع المستندات، حيث يمكن أن تحتوي كل فئة مشتقة على وظائف مشتركة مثل “فتح” و “حفظ” و “طباعة”.
- توسيع السلوكات المتعددة: في بعض الحالات، قد يكون لديك حاجة لفئة ترث من عدة فئات لتوسيع السلوكات المتعددة. على سبيل المثال، إذا كان لديك تطبيق لألعاب الفيديو يحتوي على شخصيات تتمتع بقدرات مختلفة مثل القفز والهجوم والدفاع، يمكنك تعريف فئات مشتقة من فئة “Character” وفئة “Jumpable” وفئة “Attackable” وفئة “Defendable” لتوسيع السلوكات المشتركة بين هذه الفئات.
مع ذلك، يجب أن تكون حذرًا عند استخدام الوراثة المتعددة، حيث يمكن أن تزيد من تتعقيد العلاقات بين الفئات وتجعل الكود أكثر صعوبة في الفهم والصيانة. يجب أن يتم استخدام الوراثة المتعددة فقط عندما يكون لديك حاجة واضحة وقوية لتوسيع السلوك أو تصنيف الكائنات.
مثال على الوراثة الأساسية:
class Animal { String name; void eat() { print('The animal is eating.'); } } class Cat extends Animal { void meow() { print('Meow!'); } } void main() { Cat cat = Cat(); cat.name = 'Tom'; cat.eat(); // يرث السلوك من الفئة الأساسية cat.meow(); // يحتوي على سلوك خاص به }
مثال على الوراثة المتعددة:
class Shape { void draw() { print('Drawing a shape.'); } } class Circle extends Shape { void draw() { print('Drawing a circle.'); } } class Square extends Shape { void draw() { print('Drawing a square.'); } } class SquareCircle extends Square { void draw() { super.draw(); // يتم استدعاء السلوك من الفئة الأساسية (Shape) print('Drawing a circle inside a square.'); } } void main() { SquareCircle squareCircle = SquareCircle(); squareCircle.draw(); // يرث سلوك من الفئتين Square و Shape }
مثال على الوراثة لتوسيع السلوك:
class Vehicle { void move() { print('Moving...'); } } class Car extends Vehicle { void move() { super.move(); // يتم استدعاء السلوك من الفئة الأساسية (Vehicle) print('Driving a car.'); } } class Bicycle extends Vehicle { void move() { super.move(); // يتم استدعاء السلوك من الفئة الأساسية (Vehicle) print('Riding a bicycle.'); } } void main() { Car car = Car(); car.move(); // يرث سلوك من الفئة الأساسية (Vehicle) ويوسعه بسلوك خاص للسيارة Bicycle bicycle = Bicycle(); bicycle.move(); // يرث سلوك من الفئة الأساسية (Vehicle) ويوسعه بسلوك خاص للدراجة الهوائية }
هذه الأمثلة توضح بعض الاستخدامات الشائعة للوراثة في Dart. يمكن استخدام الوراثة لتعزيز إعادة الاستخدام وتنظيم الكود وتوسيع السلوكات المشتركة بين الفئات.