الـ Mixins والـ Enums في Dart
في هذا الدرس أداتان صغيرتان تجعلان كودك أنظف: Mixin لمشاركة قدرات بين كلاسات لا تجمعها وراثة، و Enum لتعريف قائمة ثابتة من الاختيارات مثل حالات الطلب أو أيام الأسبوع. كلتاهما مستخدمتان بكثرة في تطبيقات Flutter.
المشكلة التي يحلها mixin
Section titled “المشكلة التي يحلها mixin”السمكة تسبح، والبطة تسبح وتطير، والعصفور يطير. أين نضع كود «السباحة» وكود «الطيران»؟ الكلاس يرث من أب واحد فقط، فلا تستطيع البطة أن ترث من «سابح» و«طائر» معاً. و implements لا تنفع أيضاً، لأنها لا تعطيك كوداً جاهزاً.
الحل هو Mixin: قطعة كود جاهزة تُضاف إلى أي كلاس. تخيّلها مثل إضافات البيتزا: العجينة واحدة، وتضيف عليها ما تشاء من زيتون وجبن وفطر.
mixin Swimmer { String get name; void swim() => print('$name يسبح');}
mixin Flyer { String get name; void fly() => print('$name يطير');}
class Animal { final String name; Animal(this.name);}
class Fish extends Animal with Swimmer { Fish(super.name);}
class Duck extends Animal with Swimmer, Flyer { Duck(super.name);}
void main() { var fish = Fish('السمك'); var duck = Duck('البط');
fish.swim(); duck.swim(); duck.fly(); print(duck is Flyer); print(fish is Flyer);}السمك يسبحالبط يسبحالبط يطيرtruefalseلنفهم ما حدث:
- نعرّف الإضافة بالكلمة
mixin، ونضيفها للكلاس بالكلمةwith. Duckأخذت القدرتين معاً بفاصلة بينهما، دون تكرار أي كود.- السطر
String get name;داخل الإضافة يقول: «أحتاج أن يكون في الكلاس الذي يستخدمني خاصية اسمهاname». وقد وفّرها الأبAnimal. - المعامل
isيؤكد أن البطة تُعتبرFlyerفعلاً، فيمكن وضعها في قائمة من هذا النوع.
Enum: قائمة اختيارات ثابتة
Section titled “Enum: قائمة اختيارات ثابتة”كيف تخزّن حالة طلب في متجر؟ لو استخدمت نصاً مثل 'shipped'، فأي خطأ إملائي مثل 'shiped' سيمر دون أن ينتبه أحد. Enum (اختصار enumeration، أي التعداد) يحل المشكلة: نوع جديد قيمه محددة مسبقاً، ولا يقبل غيرها.
enum OrderStatus { pending, shipped, delivered }
void main() { var status = OrderStatus.shipped;
print(status); print(status.name); print(status.index); print(OrderStatus.values.length);
var message = switch (status) { OrderStatus.pending => 'طلبك قيد التجهيز', OrderStatus.shipped => 'طلبك في الطريق', OrderStatus.delivered => 'تم التسليم', }; print(message);}OrderStatus.shippedshipped13طلبك في الطريقnameتعطي اسم القيمة نصاً، وindexترتيبها بدءاً من صفر.OrderStatus.valuesقائمة بكل القيم، وهي مفيدة لعرض الاختيارات في قائمة منسدلة.- أهم ميزة تظهر مع
switch: إذا نسيت حالة واحدة، سيرفض المحرر الكود. وإذا أضفت غداً حالة رابعةcancelled، فسيدلّك المحرر على كل مكان يحتاج تحديثاً.
Enum بخصائص وتوابع
Section titled “Enum بخصائص وتوابع”قيم الـ enum يمكن أن تحمل بيانات. هذا enum لأحجام الأكواب في مقهى، ولكل حجم اسم عربي وسعر:
enum CupSize { small('صغير', 15), medium('وسط', 20), large('كبير', 27);
final String label; final int price; const CupSize(this.label, this.price);
int priceFor(int count) => price * count;}
void main() { for (var size in CupSize.values) { print('${size.label}: ${size.price} جنيه'); }
print(CupSize.large.priceFor(3)); print(CupSize.values.byName('medium').label);}صغير: 15 جنيهوسط: 20 جنيهكبير: 27 جنيه81وسطقواعد هذه الصيغة: القيم تُكتب أولاً وتنتهي بفاصلة منقوطة، والخصائص كلها final، والمُنشئ يبدأ بـ const. والتابع byName يحوّل نصاً إلى قيمة enum، وهو مفيد عند قراءة البيانات من JSON.
جرّب بنفسك
Section titled “جرّب بنفسك”عرّف enum اسمه Level لمستويات لعبة: easy و normal و hard، ولكل مستوى عدد المحاولات المسموحة (10 و 7 و 4). ثم اطبع كل المستويات، واطبع بـ switch رسالة للمستوى الصعب:
easy: 10 محاولاتnormal: 7 محاولاتhard: 4 محاولاتحظاً موفقاً، ستحتاجه!أخطاء شائعة
Section titled “أخطاء شائعة”- استخدام
extendsمع mixin: الإضافة تُستخدم بـwithوليسextends. - إنشاء كائن من mixin:
Swimmer()مرفوضة. الإضافة ليست كلاساً كاملاً. - مقارنة enum بنص:
status == 'shipped'دائماًfalse. قارن بالقيمة نفسهاOrderStatus.shipped، أو استخدمstatus.name. - الاعتماد على
indexفي التخزين: إذا حفظت الرقم ثم غيّرت ترتيب القيم لاحقاً، فستتغير معاني البيانات القديمة. احفظnameبدلاً منه.
اختبر نفسك
Section titled “اختبر نفسك”سؤال
بأي كلمة نضيف mixin إلى كلاس؟
with تضيف كود الـ mixin إلى الكلاس، ويمكن إضافة أكثر من واحد بفواصل.
سؤال
في enum Color { red, green, blue }، ما قيمة Color.green.index؟
الترتيب يبدأ من صفر: red = 0 و green = 1 و blue = 2.
الخلاصة
Section titled “الخلاصة”mixinقطعة كود جاهزة تُضاف إلى الكلاسات بالكلمةwith.- الكلاس يرث من أب واحد، لكنه يأخذ أي عدد من الـ mixins.
enumنوع قيمه محددة مسبقاً، ويحميك من الأخطاء الإملائية.switchمع enum يجبرك على تغطية كل الحالات.- قيم الـ enum يمكن أن تحمل خصائص وتوابع.