الوسم @override
جزء من قسم Object Oriented Programming في رحلة Dart على Coddy — الدرس 39 من 110.
يُعد التعليق @override علامة من علامات أفضل الممارسات التي تخبر Dart (والمطورين الآخرين) بأنك تقوم عمداً باستبدال دالة (method) من فئة أب (parent class). وبينما يعمل تجاوز الدوال (method overriding) بدونه، فإن استخدام @override يضيف شبكة أمان مهمة.
class Animal {
String name;
Animal(this.name);
void speak() {
print('$name makes a sound');
}
}
class Dog extends Animal {
Dog(String name) : super(name);
@override
void speak() {
print('$name barks: Woof!');
}
}يوفر هذا التعليق التوضيحي (annotation) فائدتين رئيسيتين. أولاً، يوثق نيتك بوضوح - فأي شخص يقرأ الكود سيعرف على الفور أن هذه الطريقة (method) موجودة في الفئة الأب (parent class). ثانياً، سيقوم محلل Dart (Dart's analyzer) بتنبيهك إذا كانت الطريقة الأب غير موجودة بالفعل، مما يساعد في اكتشاف الأخطاء الإملائية وأخطاء إعادة هيكلة الكود (refactoring) في وقت مبكر.
تأمل ما يحدث إذا أخطأت في كتابة اسم الطريقة بدون @override:
class Cat extends Animal {
Cat(String name) : super(name);
void speek() { // خطأ إملائي! لا يوجد تحذير بدون @override
print('$name meows');
}
}بدون هذا التعليق التوضيحي (annotation)، يفترض Dart أنك تقوم بإنشاء طريقة (method) جديدة تسمى speek(). وباستخدام @override، سيقوم المحلل (analyzer) فوراً بالتنبيه إلى عدم وجود مثل هذه الطريقة في الفئة الأب (parent class). يمنع هذا التعليق التوضيحي البسيط الأخطاء الخفية التي قد يصعب تتبعها لاحقاً.
التحدي
سهللنقم ببناء نظام مشغل وسائط يوضح أهمية التعليق التوضيحي @override عند تخصيص السلوك الموروث. ستقوم بإنشاء أنواع وسائط مختلفة يقوم كل منها بتشغيل المحتوى بطريقته الخاصة.
ستقوم بتنظيم الكود الخاص بك في ملفين:
media.dart: حدد التسلسل الهرمي للوسائط هنا:- فئة
Mediaمع خاصيةString title، ومنشئ (constructor) يأخذ العنوان، وطريقةplay()تطبعPlaying: [title] - فئة
Songترث منMediaوتضيف خاصيةString artist. استخدم التعليق التوضيحي@overrideوقم بإعادة كتابةplay()لطباعةPlaying song: [title] by [artist] - فئة
Videoترث منMediaوتضيف خاصيةint duration(بالثواني). استخدم@overrideوقم بإعادة كتابةplay()لاستدعاء نسخة الفئة الأب أولاً باستخدامsuper.play()، ثم طباعةDuration: [duration] seconds
- فئة
main.dart: استورد ملف الوسائط الخاص بك واستعرض الطرق التي تمت إعادة كتابتها:- أنشئ كائن
Mediaبالعنوان'Generic Content'واستدعِplay() - اطبع سطراً فارغاً
- أنشئ
Songبالعنوان'Imagine'والفنان'John Lennon'، ثم استدعِplay() - اطبع سطراً فارغاً
- أنشئ
Videoبالعنوان'Dart Tutorial'ومدة360، ثم استدعِplay()
- أنشئ كائن
يوثق التعليق التوضيحي @override نيتك ويساعد في اكتشاف الأخطاء. إذا أخطأت في كتابة play() كـ plya()، فسينبهك التعليق التوضيحي إلى عدم وجود مثل هذه الطريقة في الفئة الأب.
المخرجات المتوقعة:
Playing: Generic Content
Playing song: Imagine by John Lennon
Playing: Dart Tutorial
Duration: 360 secondsورقة مرجعية
تُستخدم العلامة التوضيحية @override لتمييز الدوال التي تستبدل عمدًا دالة موجودة في الفئة الأب (parent class). ورغم أنها اختيارية، إلا أنها توفر الأمان والوضوح.
class Animal {
String name;
Animal(this.name);
void speak() {
print('$name makes a sound');
}
}
class Dog extends Animal {
Dog(String name) : super(name);
@override
void speak() {
print('$name barks: Woof!');
}
}فوائد @override:
- توثيق القصد - توضح أن الدالة موجودة بالفعل في الفئة الأب
- اكتشاف الأخطاء - يقوم محلل Dart (analyzer) بإصدار تحذير إذا كانت الدالة الأب غير موجودة، مما يمنع الأخطاء الإملائية
بدون @override، تؤدي الأخطاء الإملائية إلى إنشاء دوال جديدة بدلاً من التنبيه بوجود خطأ:
class Cat extends Animal {
Cat(String name) : super(name);
void speek() { // Typo creates new method without warning
print('$name meows');
}
}يمكنك استدعاء نسخة الفئة الأب باستخدام super:
@override
void play() {
super.play(); // Call parent method first
print('Additional behavior');
}جرّب بنفسك
import 'media.dart';
void main() {
// TODO: أنشئ كائن Media بالعنوان 'Generic Content' واستدعِ ()play
// TODO: اطبع سطرًا فارغًا
// TODO: أنشئ Song بالعنوان 'Imagine' والفنان 'John Lennon'، ثم استدعِ ()play
// TODO: اطبع سطرًا فارغًا
// TODO: أنشئ Video بالعنوان 'Dart Tutorial' ومدة 360، ثم استدعِ ()play
}
يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.
جميع دروس Object Oriented Programming
1أساسيات الـ OOP
الملفات الخارجيةالمكتبات والـ Importsمقدمة في الـ OOPالـ Classes مقابل الـ Objectsالكلمة المفتاحية thisالـ Methodsمتغيرات الـ Instanceأساسيات الـ Constructorمراجعة - آلة حاسبة بسيطة4أمان الـ Null
مقدمة في أمان الـ NullNullable مقابل Non-Nullableعاملا الـ ? و !الكلمة المفتاحية Late وأمان الـ Nullالعوامل المدركة للـ Nullأمان الـ Null في الـ Classesمراجعة - نظام ملف تعريف المستخدم7الفئات المجردة والواجهات
الفئات المجردةالدوال المجردةالواجهات في Dartالواجهات الضمنيةالتنفيذ مقابل التوسيعالواجهات المتعددةمراجعة - حاسبة الأشكال10المجموعات والأنماط العامة
نظرة عامة على List و Set و Mapالمجموعات آمنة النوعالفئات العامةالدوال العامةقيود الأنماط العامةIterable و Iteratorملخص - التخزين العام13مفاهيم OOP المتقدمة
التركيب مقابل الوراثة (Composition vs Inheritance)دوال التوسعة (Extension Methods)الأصناف القابلة للاستدعاء (Callable Classes)الأصناف المختومة (Sealed Classes - Dart 3)السجلات (Records - Dart 3)الأنماط والمطابقة (Patterns & Matching - 3.0)التعدادات مع الدوال (Enums with Methods)2المنشئات في Dart
المنشئ الافتراضيالمنشئات المسماةقوائم التهيئةالمنشئات الثابتةالمنشئات المصنعيةالمنشئات الموجهةملخص - بناء الأشكال5التغليف (Encapsulation)
الأعضاء العامة مقابل الخاصةاتفاقية البادئة _الخصوصية على مستوى المكتبةتعمق في Getters و Settersإخفاء المعلوماتمراجعة - سجلات الطلاب8Mixins
مقدمة إلى Mixinsإنشاء Mixinsاستخدام Mixins متعددةالكلمة المفتاحية on في MixinsMixin مقابل الوراثة (Inheritance)Mixin مقابل الواجهة (Interface)مراجعة - نظام الحيوانات (Animal System)3خصائص الـ Class
أعضاء الـ Instance مقابل الـ Staticحقول Final و Constمتغيرات Lateالـ Static Methods والحقولدوال الـ Getters والـ Settersمراجعة - مدير الحساب البنكي6الوراثة
أساسيات الوراثةالكلمة المفتاحية superإعادة تعريف الدوال (Method Overriding)الوسم @overrideالكلمة المفتاحية final Classالدوال البانية والوراثةمراجعة - هيكلية الموظفين9تعدد الأشكال
أساسيات تعدد الأشكالتعدد الأشكال عبر الـ Interfacesالتحقق من النوع أثناء التشغيلالمعاملات is و asالكلمة المفتاحية Covariantملخص - معالج الدفع12البرمجة الكائنية غير المتزامنة (Async OOP)
Futures و async/awaitأساسيات الـ Streamsمتحكمات الـ Streamsالـ Constructors غير المتزامنةالعمليات غير المتزامنة في دوال الكلاسمراجعة - جالب البيانات (Data Fetcher)15أنماط التصميم - الجزء الثاني
نمط الأمرنمط المحولنمط المزيننمط طريقة القالبنمط الحالةنمط التركيبنمط المستودع