
مفهوم overriding في Dart
نحن نعلم أنه يمكننا الوصول إلى methods الموجودة في الطبقة الفائقة من الكلاسات الابن من خلال استخدام الكلمة الأساسية super أو ببساطة عن طريق إنشاء كائنات من الكلاس الابن.
و على الرغم من ذلك قد تكون هناك مناسبات مختلفة عندما نريد أن يقوم كائن الابن بعمل الأشياء بشكل مختلف عن نفس الطريقة عند الاستدعاء باستخدام كائنات الابن يمكننا تحقيق ذلك من خلال تحديد نفس methods مرة أخرى في الابن بنفس الاسم ونفس الوسائط ونفس نوع الإرجاع كما هو الحال في نفس methods الموجودة داخل الابن.
عندما نستدعي الـ methods سيتم استدعاء methods الموجودة في الابن وتنفيذها بدلاً من methods المحددة في الابن و تُعرف هذه العملية التي حددنا فيها methods بنفس الاسم ونفس الوسيطة ونفس نوع الإرجاع في الابن الموجودة بالفعل في الطبقة الفائقة باسم تجاوز الطريقة overriding.
في لغة Dart، يُطلق مصطلح “overriding” عندما يقوم الفصل المشتق (subclass) بتعديل (أو استبدال) تنفيذ (implementation) الوظيفة (method) الموروثة (inherited) من الفصل الأساسي (superclass). في الأساس، إذا قمت بتعريف وظيفة في الفصل المشتق بنفس اسم وتوقيع الوظيفة في الفصل الأساسي، فإنها تعتبر overriding لهذه الوظيفة.
قواعد دالة Overriding
- الطريقة التي نريد تجاوزها يمكن كتابتها فقط في الابن وليس في نفس الكلاس.
- يجب أن يكون نوع الإرجاع مطابقا لـ method الموجودة في الطبقة العليا.
- يجب أن تكون قائمة argument هي نفسها الموجودة في method الموجودة في الطبقة العليا.
- لا يمكننا تجاوز method إذا تم إعلانها ثابتة أو نهائية.
- إذا لم نرث method فلا يمكننا تجاوزها.
فيما يلي مثال بسيط يوضح مفهوم overriding في Dart:
class Animal { void makeSound() { print('Animal is making a sound'); } } class Cat extends Animal { @override void makeSound() { print('Meow!'); } } void main() { Animal animal = Animal(); animal.makeSound(); // Output: Animal is making a sound Cat cat = Cat(); cat.makeSound(); // Output: Meow! }
في المثال أعلاه، لدينا فصل أساسي يسمى Animal يحتوي على وظيفة makeSound() التي تعرض نصًا بسيطًا. ثم، لدينا فصل مشتق يسمى Cat يمتد من Animal ويحتوي على وظيفة makeSound() المعتمدة. تم استخدام الكلمة الأساسية @override قبل تعريف وظيفة makeSound() في الفصل المشتق للإشارة إلى أنها overriding.
عند تشغيل البرنامج، سيتم استدعاء وظيفة makeSound() بناءً على نوع الكائن الذي يتم استخدامه. عندما يتم استدعاء makeSound() على كائن من نوع Animal، سيتم عرض “Animal is making a sound”، أما عندما يتم استدعاءه على كائن من نوع Cat (الفصل المشتق)، سيتم عرض “Meow!”.
هذا هو مفهوم overriding في Dart، حيث يمكن للفصل المشتق استبدال سلوك الوظيفة الموروثة من الفصل الأساسي.
مثال
class Shape { void draw() { print('Drawing a shape'); } } class Circle extends Shape { @override void draw() { print('Drawing a circle'); } } void main() { Shape shape = Shape
في هذا المثال، لدينا فصل Shape يحتوي على وظيفة draw() التي تقوم بطباعة “Drawing a shape”. ثم لدينا فصل Circle يمتد من Shape ويحتوي على overriding لوظيفة draw() لتطبع “Drawing a circle”. عند استدعاء الوظيفة draw() على كائن من نوع Shape، يتم استدعاء الوظيفة الموجودة في Shape، وعند استدعاءها على كائن من نوع Circle، يتم استدعاء الوظيفة المعتمدة في Circle.
مثال
class Vehicle { void startEngine() { print('Starting the engine'); } } class Car extends Vehicle { @override void startEngine() { super.startEngine(); print('Accelerating'); } } void main() { Car car = Car(); car.startEngine(); }
في هذا المثال، لدينا فصل Vehicle يحتوي على وظيفة startEngine() التي تقوم بطباعة “Starting the engine”. ثم لدينا فصل Car يمتد من Vehicle ويحتوي على overriding لوظيفة startEngine() لطباعة “Accelerating” بعد استدعاء وظيفة startEngine() في الفصل الأساسي باستخدام super.startEngine(). هذا يعني أن الوظيفة المعتمدة في Car تقوم بتوسيع سلوك الوظيفة الموجودة في Vehicle.
هذه هي بعض الأمثلة البسيطة التي توضح مفهوم overriding في لغة Dart. من خلال overriding، يمكن للفصول المشتقة تعديل سلوك الوظائف الموروثة من الفصول الأساسية لتتناسب مع احتياجاتها الخاصة.
مثال
class Animal { void makeSound() { print('Animal is making a sound'); } } class Cat extends Animal { @override void makeSound() { super.makeSound(); print('Meow!'); } } class Dog extends Animal { @override void makeSound() { super.makeSound(); print('Woof!'); } } void main() { Cat cat = Cat(); cat.makeSound(); // Output: // Animal is making a sound // Meow! Dog dog = Dog(); dog.makeSound(); // Output: // Animal is making a sound // Woof! }
في هذا المثال، لدينا فصل Animal يحتوي على وظيفة makeSound() التي تقوم بطباعة “Animal is making a sound”. ثم لدينا فصل Cat و Dog يمتدان من Animal ويحتويان على overriding لوظيفة makeSound() لطباعة صوت القطة والكلب على التوالي. باستخدام super.makeSound()، يتم استدعاء وتنفيذ الوظيفة الموجودة في Animal أولاً قبل إضافة سلوك الوظيفة المعتمدة في الفصول المشتقة.
مثال
class Shape { void draw() { print('Drawing a shape'); } } class Circle extends Shape { @override void draw() { super.draw(); print('Drawing a circle'); } } class Square extends Shape { @override void draw() { super.draw(); print('Drawing a square'); } } void main() { Circle circle = Circle(); circle.draw(); // Output: // Drawing a shape // Drawing a circle Square square = Square(); square.draw(); // Output: // Drawing a shape // Drawing a square }
في هذا المثال، لدينا فصل Shape يحتوي على وظيفة draw() التي تقوم بطباعة “Drawing a shape”. ثم لدينا فصل Circle و Square يمتدان من Shape ويحتويان على overriding لوظيفة draw() لطباعة الشكل المحدد (دائرة أو مربع) بالإضافة إلى رسم الشكل الأساسي. باستخدام super.draw()، يتم استدعاء وتنفيذ الوظيفة الموجودة في Shape أولاً قبل رسم الشكل المعتمد في الفصول المشتقة.
مثال
class ParentClass { void displayName(){ print("Inside the Parent class Method"); } } class ChildClass extends ParentClass { void displayName(){ print("Inside the Child class Method"); } } void main(){ ParentClass pClassObject = new ParentClass(); ChildClass cClassObject = new ChildClass(); pClassObject.displayName(); cClassObject.displayName(); }
في المثال أعلاه لدينا كلاسين و هما ParentClass و ChildClass و الكلاس ParentClass هو كلاس superclass و ChildClass هو الكلاس الفرعي الذي يرث superclass
وقد أعلنا عنه باسم displayName في كل الكلاسات و داخل الدالة الرئيسية و عند تشغيل الكود ستكون نتيجة
Inside the Parent class Method Inside the Child class Method